Last active
May 4, 2021 09:25
-
-
Save NobuoTsukamoto/ba7028bd4e77c1431f4ff560ad61e8d7 to your computer and use it in GitHub Desktop.
Training TF1 Detection Model SSDLite Mobilenet V3
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"nbformat": 4, | |
"nbformat_minor": 0, | |
"metadata": { | |
"colab": { | |
"name": "Training TF1 Detection Model SSDLite Mobilenet V3", | |
"provenance": [], | |
"collapsed_sections": [], | |
"toc_visible": true, | |
"machine_shape": "hm", | |
"include_colab_link": true | |
}, | |
"kernelspec": { | |
"name": "python3", | |
"display_name": "Python 3" | |
}, | |
"accelerator": "GPU" | |
}, | |
"cells": [ | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"id": "view-in-github", | |
"colab_type": "text" | |
}, | |
"source": [ | |
"<a href=\"https://colab.research.google.com/gist/NobuoTsukamoto/ba7028bd4e77c1431f4ff560ad61e8d7/training-tf1-detection-model-ssdlite-mobilenet-v3.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"id": "SEv4Nu2G8OFa" | |
}, | |
"source": [ | |
"# Training with TensorFlow 1 Detection Model\n", | |
"\n", | |
"This notebook provides steps for fine-tuning with the TensorFlow 1 Object detection API. Since the release of the TensorFlow 2 Object detection API, training with the TF 1 Object detection API has been unclear. Also, fine-tuning is not working with the latest commits([8454ded](https://github.com/tensorflow/models/commit/8454dedc4c0dc5eeea0f67c443fb9da32fcb7733)) as of 2021.05.02. \n", | |
"Work around the bug and use the Oxford-IIIT Pet Dataset to fine-tune the SSDLite MobileNet V3 Large. Finally, export the model and TF-Lite model.\n", | |
"\n", | |
"Reference:\n", | |
"- [Object Detection API with TensorFlow 1](https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/tf1.md)\n", | |
"- [Training and Evaluation with TensorFlow 1](https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/tf1_training_and_evaluation.md)\n", | |
"- [Running on mobile with TensorFlow Lite](https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/running_on_mobile_tensorflowlite.md)\n", | |
"- [cant load pre-train models #8695](https://github.com/tensorflow/models/issues/8695)\n", | |
"- [Transfer Learning with the MobileDet checkpoints #9641](https://github.com/tensorflow/models/issues/9641)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"id": "9TBgUmdwF8zw" | |
}, | |
"source": [ | |
"MIT License\n", | |
"\n", | |
"Copyright (c) 2019 Nobuo Tsukamoto\n", | |
"\n", | |
"Permission is hereby granted, free of charge, to any person obtaining a copy\n", | |
"of this software and associated documentation files (the \"Software\"), to deal\n", | |
"in the Software without restriction, including without limitation the rights\n", | |
"to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n", | |
"copies of the Software, and to permit persons to whom the Software is\n", | |
"furnished to do so, subject to the following conditions:\n", | |
"\n", | |
"The above copyright notice and this permission notice shall be included in all\n", | |
"copies or substantial portions of the Software.\n", | |
"\n", | |
"THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n", | |
"IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n", | |
"FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n", | |
"AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n", | |
"LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n", | |
"OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n", | |
"SOFTWARE." | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"id": "t67NpfjyBTnF" | |
}, | |
"source": [ | |
"# Install TensorFlow 1.x" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"metadata": { | |
"id": "45JZXAHmjdnf", | |
"colab": { | |
"base_uri": "https://localhost:8080/" | |
}, | |
"outputId": "f415c41a-9a19-4079-a41d-522fc3f77600" | |
}, | |
"source": [ | |
"%tensorflow_version 1.x" | |
], | |
"execution_count": 1, | |
"outputs": [ | |
{ | |
"output_type": "stream", | |
"text": [ | |
"TensorFlow 1.x selected.\n" | |
], | |
"name": "stdout" | |
} | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"id": "6QB2TaR4AKve" | |
}, | |
"source": [ | |
"# Check TensorFlow and NVIDIA CUDA version." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"metadata": { | |
"colab": { | |
"base_uri": "https://localhost:8080/", | |
"height": 34 | |
}, | |
"id": "7zCUA1hiPlWV", | |
"outputId": "121c0ad7-a568-47b3-d843-72d0d2bfc58d" | |
}, | |
"source": [ | |
"import tensorflow as tf\n", | |
"tf.__version__" | |
], | |
"execution_count": 2, | |
"outputs": [ | |
{ | |
"output_type": "execute_result", | |
"data": { | |
"application/vnd.google.colaboratory.intrinsic+json": { | |
"type": "string" | |
}, | |
"text/plain": [ | |
"'1.15.2'" | |
] | |
}, | |
"metadata": { | |
"tags": [] | |
}, | |
"execution_count": 2 | |
} | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"metadata": { | |
"id": "CnhOFVQTzoWD", | |
"colab": { | |
"base_uri": "https://localhost:8080/" | |
}, | |
"outputId": "62dfd8c8-3a77-4e6a-9208-c09a8f2b594d" | |
}, | |
"source": [ | |
"!nvidia-smi" | |
], | |
"execution_count": 3, | |
"outputs": [ | |
{ | |
"output_type": "stream", | |
"text": [ | |
"Mon May 3 22:43:22 2021 \n", | |
"+-----------------------------------------------------------------------------+\n", | |
"| NVIDIA-SMI 465.19.01 Driver Version: 460.32.03 CUDA Version: 11.2 |\n", | |
"|-------------------------------+----------------------+----------------------+\n", | |
"| GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC |\n", | |
"| Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. |\n", | |
"| | | MIG M. |\n", | |
"|===============================+======================+======================|\n", | |
"| 0 Tesla P100-PCIE... Off | 00000000:00:04.0 Off | 0 |\n", | |
"| N/A 37C P0 27W / 250W | 0MiB / 16280MiB | 0% Default |\n", | |
"| | | N/A |\n", | |
"+-------------------------------+----------------------+----------------------+\n", | |
" \n", | |
"+-----------------------------------------------------------------------------+\n", | |
"| Processes: |\n", | |
"| GPU GI CI PID Type Process name GPU Memory |\n", | |
"| ID ID Usage |\n", | |
"|=============================================================================|\n", | |
"| No running processes found |\n", | |
"+-----------------------------------------------------------------------------+\n" | |
], | |
"name": "stdout" | |
} | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"id": "y_owSZjMHN4-" | |
}, | |
"source": [ | |
"# Install TensorFlow 1 Object detection API\n", | |
"\n", | |
"Note:\n", | |
"- It is not included in [Python Package Installation](https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/tf1.md) procedure, but you need to install the tensorflow-addons module.\n", | |
"- Modify model_lib.py to avoid a bug where fine_tune_checkpoint is not enabled. \n", | |
"See the comment on bug [#8695](https://github.com/tensorflow/models/issues/8695#issuecomment-775456367) for more details.\n", | |
"- If you specify `--use-feature=2020-resolver` during installation, you will get an error." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"metadata": { | |
"id": "KIue85E-uRe8", | |
"colab": { | |
"base_uri": "https://localhost:8080/" | |
}, | |
"outputId": "8d583b79-a38e-48b2-b55b-6c118616769c" | |
}, | |
"source": [ | |
"%%bash\n", | |
"\n", | |
"# Install tensorflow-addons.\n", | |
"# Note: tensorflow-addons have dependencies, \n", | |
"# but they are not installed by object_detection/packages/tf1/setup.py.\n", | |
"pip install tensorflow-addons\n", | |
"\n", | |
"# Clone tensorflow/models repository.\n", | |
"git clone https://github.com/tensorflow/models.git\n", | |
"cd models\n", | |
"git show" | |
], | |
"execution_count": 4, | |
"outputs": [ | |
{ | |
"output_type": "stream", | |
"text": [ | |
"Collecting tensorflow-addons\n", | |
" Downloading https://files.pythonhosted.org/packages/74/e3/56d2fe76f0bb7c88ed9b2a6a557e25e83e252aec08f13de34369cd850a0b/tensorflow_addons-0.12.1-cp37-cp37m-manylinux2010_x86_64.whl (703kB)\n", | |
"Requirement already satisfied: typeguard>=2.7 in /usr/local/lib/python3.7/dist-packages (from tensorflow-addons) (2.7.1)\n", | |
"Installing collected packages: tensorflow-addons\n", | |
"Successfully installed tensorflow-addons-0.12.1\n", | |
"commit d7a784e6a85528292abf960be3a7cc643fa2b02c\n", | |
"Author: Yu-hui Chen <[email protected]>\n", | |
"Date: Mon May 3 14:54:11 2021 -0700\n", | |
"\n", | |
" Fixed the bug in the model builder of not setting the default values properly\n", | |
" for the object center head parameters in CenterNet.\n", | |
" \n", | |
" PiperOrigin-RevId: 371783912\n", | |
"\n", | |
"diff --git a/research/object_detection/builders/model_builder.py b/research/object_detection/builders/model_builder.py\n", | |
"index 949618f8..08c65e07 100644\n", | |
"--- a/research/object_detection/builders/model_builder.py\n", | |
"+++ b/research/object_detection/builders/model_builder.py\n", | |
"@@ -946,7 +946,7 @@ def object_center_proto_to_params(oc_config):\n", | |
" if oc_config.keypoint_weights_for_center:\n", | |
" keypoint_weights_for_center = list(oc_config.keypoint_weights_for_center)\n", | |
" \n", | |
"- if oc_config.center_head_params:\n", | |
"+ if oc_config.HasField('center_head_params'):\n", | |
" center_head_num_filters = list(oc_config.center_head_params.num_filters)\n", | |
" center_head_kernel_sizes = list(oc_config.center_head_params.kernel_sizes)\n", | |
" else:\n", | |
"diff --git a/research/object_detection/builders/model_builder_tf2_test.py b/research/object_detection/builders/model_builder_tf2_test.py\n", | |
"index 4bcc6dd6..9d386fdd 100644\n", | |
"--- a/research/object_detection/builders/model_builder_tf2_test.py\n", | |
"+++ b/research/object_detection/builders/model_builder_tf2_test.py\n", | |
"@@ -18,6 +18,7 @@\n", | |
" import os\n", | |
" import unittest\n", | |
" \n", | |
"+from absl.testing import parameterized\n", | |
" import tensorflow.compat.v1 as tf\n", | |
" \n", | |
" from google.protobuf import text_format\n", | |
"@@ -32,7 +33,8 @@ from object_detection.utils import tf_version\n", | |
" \n", | |
" \n", | |
" @unittest.skipIf(tf_version.is_tf1(), 'Skipping TF2.X only test.')\n", | |
"-class ModelBuilderTF2Test(model_builder_test.ModelBuilderTest):\n", | |
"+class ModelBuilderTF2Test(\n", | |
"+ model_builder_test.ModelBuilderTest, parameterized.TestCase):\n", | |
" \n", | |
" def default_ssd_feature_extractor(self):\n", | |
" return 'ssd_resnet50_v1_fpn_keras'\n", | |
"@@ -79,7 +81,7 @@ class ModelBuilderTF2Test(model_builder_test.ModelBuilderTest):\n", | |
" f.write(keypoint_spec_text)\n", | |
" return keypoint_label_map_path\n", | |
" \n", | |
"- def get_fake_keypoint_proto(self):\n", | |
"+ def get_fake_keypoint_proto(self, customize_head_params=False):\n", | |
" task_proto_txt = \"\"\"\n", | |
" task_name: \"human_pose\"\n", | |
" task_loss_weight: 0.9\n", | |
"@@ -120,18 +122,27 @@ class ModelBuilderTF2Test(model_builder_test.ModelBuilderTest):\n", | |
" predict_depth: true\n", | |
" per_keypoint_depth: true\n", | |
" keypoint_depth_loss_weight: 0.3\n", | |
"+ \"\"\"\n", | |
"+ if customize_head_params:\n", | |
"+ task_proto_txt += \"\"\"\n", | |
" heatmap_head_params {\n", | |
" num_filters: 64\n", | |
" num_filters: 32\n", | |
" kernel_sizes: 5\n", | |
" kernel_sizes: 3\n", | |
" }\n", | |
"- \"\"\"\n", | |
"+ offset_head_params {\n", | |
"+ num_filters: 128\n", | |
"+ num_filters: 64\n", | |
"+ kernel_sizes: 5\n", | |
"+ kernel_sizes: 3\n", | |
"+ }\n", | |
"+ \"\"\"\n", | |
" config = text_format.Merge(task_proto_txt,\n", | |
" center_net_pb2.CenterNet.KeypointEstimation())\n", | |
" return config\n", | |
" \n", | |
"- def get_fake_object_center_proto(self):\n", | |
"+ def get_fake_object_center_proto(self, customize_head_params=False):\n", | |
" proto_txt = \"\"\"\n", | |
" object_center_loss_weight: 0.5\n", | |
" heatmap_bias_init: 3.14\n", | |
"@@ -143,13 +154,16 @@ class ModelBuilderTF2Test(model_builder_test.ModelBuilderTest):\n", | |
" beta: 4.0\n", | |
" }\n", | |
" }\n", | |
"+ \"\"\"\n", | |
"+ if customize_head_params:\n", | |
"+ proto_txt += \"\"\"\n", | |
" center_head_params {\n", | |
" num_filters: 64\n", | |
" num_filters: 32\n", | |
" kernel_sizes: 5\n", | |
" kernel_sizes: 3\n", | |
" }\n", | |
"- \"\"\"\n", | |
"+ \"\"\"\n", | |
" return text_format.Merge(proto_txt,\n", | |
" center_net_pb2.CenterNet.ObjectCenterParams())\n", | |
" \n", | |
"@@ -222,7 +236,11 @@ class ModelBuilderTF2Test(model_builder_test.ModelBuilderTest):\n", | |
" return text_format.Merge(proto_txt,\n", | |
" center_net_pb2.CenterNet.DensePoseEstimation())\n", | |
" \n", | |
"- def test_create_center_net_model(self):\n", | |
"+ @parameterized.parameters(\n", | |
"+ {'customize_head_params': True},\n", | |
"+ {'customize_head_params': False}\n", | |
"+ )\n", | |
"+ def test_create_center_net_model(self, customize_head_params):\n", | |
" \"\"\"Test building a CenterNet model from proto txt.\"\"\"\n", | |
" proto_txt = \"\"\"\n", | |
" center_net {\n", | |
"@@ -244,11 +262,13 @@ class ModelBuilderTF2Test(model_builder_test.ModelBuilderTest):\n", | |
" # Set up the configuration proto.\n", | |
" config = text_format.Merge(proto_txt, model_pb2.DetectionModel())\n", | |
" config.center_net.object_center_params.CopyFrom(\n", | |
"- self.get_fake_object_center_proto())\n", | |
"+ self.get_fake_object_center_proto(\n", | |
"+ customize_head_params=customize_head_params))\n", | |
" config.center_net.object_detection_task.CopyFrom(\n", | |
" self.get_fake_object_detection_proto())\n", | |
" config.center_net.keypoint_estimation_task.append(\n", | |
"- self.get_fake_keypoint_proto())\n", | |
"+ self.get_fake_keypoint_proto(\n", | |
"+ customize_head_params=customize_head_params))\n", | |
" config.center_net.keypoint_label_map_path = (\n", | |
" self.get_fake_label_map_file_path())\n", | |
" config.center_net.mask_estimation_task.CopyFrom(\n", | |
"@@ -269,8 +289,12 @@ class ModelBuilderTF2Test(model_builder_test.ModelBuilderTest):\n", | |
" self.assertAlmostEqual(\n", | |
" model._center_params.heatmap_bias_init, 3.14, places=4)\n", | |
" self.assertEqual(model._center_params.max_box_predictions, 15)\n", | |
"- self.assertEqual(model._center_params.center_head_num_filters, [64, 32])\n", | |
"- self.assertEqual(model._center_params.center_head_kernel_sizes, [5, 3])\n", | |
"+ if customize_head_params:\n", | |
"+ self.assertEqual(model._center_params.center_head_num_filters, [64, 32])\n", | |
"+ self.assertEqual(model._center_params.center_head_kernel_sizes, [5, 3])\n", | |
"+ else:\n", | |
"+ self.assertEqual(model._center_params.center_head_num_filters, [256])\n", | |
"+ self.assertEqual(model._center_params.center_head_kernel_sizes, [3])\n", | |
" \n", | |
" # Check object detection related parameters.\n", | |
" self.assertAlmostEqual(model._od_params.offset_loss_weight, 0.1)\n", | |
"@@ -305,12 +329,18 @@ class ModelBuilderTF2Test(model_builder_test.ModelBuilderTest):\n", | |
" self.assertEqual(kp_params.predict_depth, True)\n", | |
" self.assertEqual(kp_params.per_keypoint_depth, True)\n", | |
" self.assertAlmostEqual(kp_params.keypoint_depth_loss_weight, 0.3)\n", | |
"- # Set by the config.\n", | |
"- self.assertEqual(kp_params.heatmap_head_num_filters, [64, 32])\n", | |
"- self.assertEqual(kp_params.heatmap_head_kernel_sizes, [5, 3])\n", | |
"- # Default values:\n", | |
"- self.assertEqual(kp_params.offset_head_num_filters, [256])\n", | |
"- self.assertEqual(kp_params.offset_head_kernel_sizes, [3])\n", | |
"+ if customize_head_params:\n", | |
"+ # Set by the config.\n", | |
"+ self.assertEqual(kp_params.heatmap_head_num_filters, [64, 32])\n", | |
"+ self.assertEqual(kp_params.heatmap_head_kernel_sizes, [5, 3])\n", | |
"+ self.assertEqual(kp_params.offset_head_num_filters, [128, 64])\n", | |
"+ self.assertEqual(kp_params.offset_head_kernel_sizes, [5, 3])\n", | |
"+ else:\n", | |
"+ # Default values:\n", | |
"+ self.assertEqual(kp_params.heatmap_head_num_filters, [256])\n", | |
"+ self.assertEqual(kp_params.heatmap_head_kernel_sizes, [3])\n", | |
"+ self.assertEqual(kp_params.offset_head_num_filters, [256])\n", | |
"+ self.assertEqual(kp_params.offset_head_kernel_sizes, [3])\n", | |
" \n", | |
" # Check mask related parameters.\n", | |
" self.assertAlmostEqual(model._mask_params.task_loss_weight, 0.7)\n" | |
], | |
"name": "stdout" | |
}, | |
{ | |
"output_type": "stream", | |
"text": [ | |
"Cloning into 'models'...\n" | |
], | |
"name": "stderr" | |
} | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"metadata": { | |
"id": "x8kQDJH55inl" | |
}, | |
"source": [ | |
"%%bash\n", | |
"\n", | |
"cat <<EOF > /content/model_lib.patch\n", | |
"diff --git a/research/object_detection/model_lib.py b/research/object_detection/model_lib.py\n", | |
"index 111be9cb..bd8ab197 100644\n", | |
"--- a/research/object_detection/model_lib.py\n", | |
"+++ b/research/object_detection/model_lib.py\n", | |
"@@ -497,7 +497,7 @@ def create_model_fn(detection_model_fn, configs, hparams=None, use_tpu=False,\n", | |
" features[fields.InputDataFields.true_image_shape]))\n", | |
" \n", | |
" if mode == tf.estimator.ModeKeys.TRAIN:\n", | |
"- load_pretrained = hparams.load_pretrained if hparams else False\n", | |
"+ load_pretrained = hparams.load_pretrained if hparams else True\n", | |
" if train_config.fine_tune_checkpoint and load_pretrained:\n", | |
" if not train_config.fine_tune_checkpoint_type:\n", | |
" # train_config.from_detection_checkpoint field is deprecated. For\n", | |
"@@ -507,6 +507,14 @@ def create_model_fn(detection_model_fn, configs, hparams=None, use_tpu=False,\n", | |
" train_config.fine_tune_checkpoint_type = 'detection'\n", | |
" else:\n", | |
" train_config.fine_tune_checkpoint_type = 'classification'\n", | |
"+\n", | |
"+ tf.logging.info('fine tuning parameter: fine_tune_checkpoint_type %s, '\n", | |
"+ 'from_detection_checkpoint %s fine_tune_checkpoint %s '\n", | |
"+ 'load_all_detection_checkpoint_vars %s',\n", | |
"+ train_config.fine_tune_checkpoint_type,\n", | |
"+ train_config.from_detection_checkpoint,\n", | |
"+ train_config.fine_tune_checkpoint,\n", | |
"+ train_config.load_all_detection_checkpoint_vars)\n", | |
" asg_map = detection_model.restore_map(\n", | |
" fine_tune_checkpoint_type=train_config.fine_tune_checkpoint_type,\n", | |
" load_all_detection_checkpoint_vars=(\n", | |
"EOF" | |
], | |
"execution_count": 5, | |
"outputs": [] | |
}, | |
{ | |
"cell_type": "code", | |
"metadata": { | |
"colab": { | |
"base_uri": "https://localhost:8080/" | |
}, | |
"id": "ueEgAw_s53dG", | |
"outputId": "73b077f1-a2c7-441e-bbd2-af5f4ed0d8af" | |
}, | |
"source": [ | |
"%%bash\n", | |
"\n", | |
"cd models\n", | |
"patch -p1 < /content/model_lib.patch" | |
], | |
"execution_count": 6, | |
"outputs": [ | |
{ | |
"output_type": "stream", | |
"text": [ | |
"patching file research/object_detection/model_lib.py\n" | |
], | |
"name": "stdout" | |
} | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"metadata": { | |
"colab": { | |
"base_uri": "https://localhost:8080/" | |
}, | |
"id": "3d73ABl0hluK", | |
"outputId": "b37b6587-62f7-4692-f484-edcccad0e7f4" | |
}, | |
"source": [ | |
"%%bash\n", | |
"\n", | |
"cd models/research\n", | |
"# Compile protos.\n", | |
"protoc object_detection/protos/*.proto --python_out=.\n", | |
"\n", | |
"# Install TensorFlow Object Detection API.\n", | |
"cp object_detection/packages/tf1/setup.py .\n", | |
"python -m pip install ." | |
], | |
"execution_count": 7, | |
"outputs": [ | |
{ | |
"output_type": "stream", | |
"text": [ | |
"Processing /content/models/research\n", | |
"Requirement already satisfied: pillow in /usr/local/lib/python3.7/dist-packages (from object-detection==0.1) (7.1.2)\n", | |
"Requirement already satisfied: lxml in /usr/local/lib/python3.7/dist-packages (from object-detection==0.1) (4.2.6)\n", | |
"Requirement already satisfied: matplotlib in /usr/local/lib/python3.7/dist-packages (from object-detection==0.1) (3.2.2)\n", | |
"Requirement already satisfied: Cython in /usr/local/lib/python3.7/dist-packages (from object-detection==0.1) (0.29.22)\n", | |
"Requirement already satisfied: contextlib2 in /usr/local/lib/python3.7/dist-packages (from object-detection==0.1) (0.5.5)\n", | |
"Collecting tf-slim\n", | |
" Downloading https://files.pythonhosted.org/packages/02/97/b0f4a64df018ca018cc035d44f2ef08f91e2e8aa67271f6f19633a015ff7/tf_slim-1.1.0-py2.py3-none-any.whl (352kB)\n", | |
"Requirement already satisfied: six in /usr/local/lib/python3.7/dist-packages (from object-detection==0.1) (1.15.0)\n", | |
"Requirement already satisfied: pycocotools in /usr/local/lib/python3.7/dist-packages (from object-detection==0.1) (2.0.2)\n", | |
"Collecting lvis\n", | |
" Downloading https://files.pythonhosted.org/packages/72/b6/1992240ab48310b5360bfdd1d53163f43bb97d90dc5dc723c67d41c38e78/lvis-0.5.3-py3-none-any.whl\n", | |
"Requirement already satisfied: scipy in /usr/local/lib/python3.7/dist-packages (from object-detection==0.1) (1.4.1)\n", | |
"Requirement already satisfied: pandas in /usr/local/lib/python3.7/dist-packages (from object-detection==0.1) (1.1.5)\n", | |
"Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.7/dist-packages (from matplotlib->object-detection==0.1) (0.10.0)\n", | |
"Requirement already satisfied: numpy>=1.11 in /usr/local/lib/python3.7/dist-packages (from matplotlib->object-detection==0.1) (1.19.5)\n", | |
"Requirement already satisfied: kiwisolver>=1.0.1 in /usr/local/lib/python3.7/dist-packages (from matplotlib->object-detection==0.1) (1.3.1)\n", | |
"Requirement already satisfied: pyparsing!=2.0.4,!=2.1.2,!=2.1.6,>=2.0.1 in /usr/local/lib/python3.7/dist-packages (from matplotlib->object-detection==0.1) (2.4.7)\n", | |
"Requirement already satisfied: python-dateutil>=2.1 in /usr/local/lib/python3.7/dist-packages (from matplotlib->object-detection==0.1) (2.8.1)\n", | |
"Requirement already satisfied: absl-py>=0.2.2 in /usr/local/lib/python3.7/dist-packages (from tf-slim->object-detection==0.1) (0.12.0)\n", | |
"Requirement already satisfied: setuptools>=18.0 in /usr/local/lib/python3.7/dist-packages (from pycocotools->object-detection==0.1) (56.0.0)\n", | |
"Requirement already satisfied: opencv-python>=4.1.0.25 in /usr/local/lib/python3.7/dist-packages (from lvis->object-detection==0.1) (4.1.2.30)\n", | |
"Requirement already satisfied: pytz>=2017.2 in /usr/local/lib/python3.7/dist-packages (from pandas->object-detection==0.1) (2018.9)\n", | |
"Building wheels for collected packages: object-detection\n", | |
" Building wheel for object-detection (setup.py): started\n", | |
" Building wheel for object-detection (setup.py): finished with status 'done'\n", | |
" Created wheel for object-detection: filename=object_detection-0.1-cp37-none-any.whl size=1647527 sha256=b63dc1d0f49f7e0b003b63c3d6429c8fe45af0a96d3a54a5255898f6db568f44\n", | |
" Stored in directory: /tmp/pip-ephem-wheel-cache-jo2lg33i/wheels/94/49/4b/39b051683087a22ef7e80ec52152a27249d1a644ccf4e442ea\n", | |
"Successfully built object-detection\n", | |
"Installing collected packages: tf-slim, lvis, object-detection\n", | |
"Successfully installed lvis-0.5.3 object-detection-0.1 tf-slim-1.1.0\n" | |
], | |
"name": "stdout" | |
} | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"metadata": { | |
"id": "4F3kd4rEGIgo", | |
"colab": { | |
"base_uri": "https://localhost:8080/" | |
}, | |
"outputId": "90fc1deb-eb62-4b30-8595-397b4e8df4d9" | |
}, | |
"source": [ | |
"%cd /content/models/research" | |
], | |
"execution_count": 8, | |
"outputs": [ | |
{ | |
"output_type": "stream", | |
"text": [ | |
"/content/models/research\n" | |
], | |
"name": "stdout" | |
} | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"metadata": { | |
"id": "XRfEVbHijRiq", | |
"colab": { | |
"base_uri": "https://localhost:8080/" | |
}, | |
"outputId": "20d5606d-4fa5-4848-b252-0d097ea73fc0" | |
}, | |
"source": [ | |
"import os\n", | |
"\n", | |
"os.environ['PYTHONPATH'] = '/content/models:' + os.environ['PYTHONPATH']\n", | |
"print(os.environ['PYTHONPATH'])" | |
], | |
"execution_count": 9, | |
"outputs": [ | |
{ | |
"output_type": "stream", | |
"text": [ | |
"/content/models:/tensorflow-1.15.2/python3.7:/env/python\n" | |
], | |
"name": "stdout" | |
} | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"metadata": { | |
"id": "Y_uBeLzBHHVH", | |
"colab": { | |
"base_uri": "https://localhost:8080/" | |
}, | |
"outputId": "2a6a347e-2d50-4a09-b780-3ec8d9f388de" | |
}, | |
"source": [ | |
"!python object_detection/builders/model_builder_tf1_test.py" | |
], | |
"execution_count": 10, | |
"outputs": [ | |
{ | |
"output_type": "stream", | |
"text": [ | |
"Running tests under Python 3.7.10: /usr/bin/python3\n", | |
"[ RUN ] ModelBuilderTF1Test.test_create_context_rcnn_from_config_with_params0 (True)\n", | |
"[ OK ] ModelBuilderTF1Test.test_create_context_rcnn_from_config_with_params0 (True)\n", | |
"[ RUN ] ModelBuilderTF1Test.test_create_context_rcnn_from_config_with_params1 (False)\n", | |
"[ OK ] ModelBuilderTF1Test.test_create_context_rcnn_from_config_with_params1 (False)\n", | |
"[ RUN ] ModelBuilderTF1Test.test_create_experimental_model\n", | |
"[ OK ] ModelBuilderTF1Test.test_create_experimental_model\n", | |
"[ RUN ] ModelBuilderTF1Test.test_create_faster_rcnn_from_config_with_crop_feature0 (True)\n", | |
"[ OK ] ModelBuilderTF1Test.test_create_faster_rcnn_from_config_with_crop_feature0 (True)\n", | |
"[ RUN ] ModelBuilderTF1Test.test_create_faster_rcnn_from_config_with_crop_feature1 (False)\n", | |
"[ OK ] ModelBuilderTF1Test.test_create_faster_rcnn_from_config_with_crop_feature1 (False)\n", | |
"[ RUN ] ModelBuilderTF1Test.test_create_faster_rcnn_model_from_config_with_example_miner\n", | |
"[ OK ] ModelBuilderTF1Test.test_create_faster_rcnn_model_from_config_with_example_miner\n", | |
"[ RUN ] ModelBuilderTF1Test.test_create_faster_rcnn_models_from_config_faster_rcnn_with_matmul\n", | |
"[ OK ] ModelBuilderTF1Test.test_create_faster_rcnn_models_from_config_faster_rcnn_with_matmul\n", | |
"[ RUN ] ModelBuilderTF1Test.test_create_faster_rcnn_models_from_config_faster_rcnn_without_matmul\n", | |
"[ OK ] ModelBuilderTF1Test.test_create_faster_rcnn_models_from_config_faster_rcnn_without_matmul\n", | |
"[ RUN ] ModelBuilderTF1Test.test_create_faster_rcnn_models_from_config_mask_rcnn_with_matmul\n", | |
"[ OK ] ModelBuilderTF1Test.test_create_faster_rcnn_models_from_config_mask_rcnn_with_matmul\n", | |
"[ RUN ] ModelBuilderTF1Test.test_create_faster_rcnn_models_from_config_mask_rcnn_without_matmul\n", | |
"[ OK ] ModelBuilderTF1Test.test_create_faster_rcnn_models_from_config_mask_rcnn_without_matmul\n", | |
"[ RUN ] ModelBuilderTF1Test.test_create_rfcn_model_from_config\n", | |
"[ OK ] ModelBuilderTF1Test.test_create_rfcn_model_from_config\n", | |
"[ RUN ] ModelBuilderTF1Test.test_create_ssd_fpn_model_from_config\n", | |
"[ OK ] ModelBuilderTF1Test.test_create_ssd_fpn_model_from_config\n", | |
"[ RUN ] ModelBuilderTF1Test.test_create_ssd_models_from_config\n", | |
"[ OK ] ModelBuilderTF1Test.test_create_ssd_models_from_config\n", | |
"[ RUN ] ModelBuilderTF1Test.test_invalid_faster_rcnn_batchnorm_update\n", | |
"[ OK ] ModelBuilderTF1Test.test_invalid_faster_rcnn_batchnorm_update\n", | |
"[ RUN ] ModelBuilderTF1Test.test_invalid_first_stage_nms_iou_threshold\n", | |
"[ OK ] ModelBuilderTF1Test.test_invalid_first_stage_nms_iou_threshold\n", | |
"[ RUN ] ModelBuilderTF1Test.test_invalid_model_config_proto\n", | |
"[ OK ] ModelBuilderTF1Test.test_invalid_model_config_proto\n", | |
"[ RUN ] ModelBuilderTF1Test.test_invalid_second_stage_batch_size\n", | |
"[ OK ] ModelBuilderTF1Test.test_invalid_second_stage_batch_size\n", | |
"[ RUN ] ModelBuilderTF1Test.test_session\n", | |
"[ SKIPPED ] ModelBuilderTF1Test.test_session\n", | |
"[ RUN ] ModelBuilderTF1Test.test_unknown_faster_rcnn_feature_extractor\n", | |
"[ OK ] ModelBuilderTF1Test.test_unknown_faster_rcnn_feature_extractor\n", | |
"[ RUN ] ModelBuilderTF1Test.test_unknown_meta_architecture\n", | |
"[ OK ] ModelBuilderTF1Test.test_unknown_meta_architecture\n", | |
"[ RUN ] ModelBuilderTF1Test.test_unknown_ssd_feature_extractor\n", | |
"[ OK ] ModelBuilderTF1Test.test_unknown_ssd_feature_extractor\n", | |
"----------------------------------------------------------------------\n", | |
"Ran 21 tests in 0.214s\n", | |
"\n", | |
"OK (skipped=1)\n" | |
], | |
"name": "stdout" | |
} | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"id": "iKdLuAihHhFA" | |
}, | |
"source": [ | |
"# Download Pet dataset and create tf-record" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"metadata": { | |
"id": "rZG8f4LsnpNl", | |
"colab": { | |
"base_uri": "https://localhost:8080/" | |
}, | |
"outputId": "d2081229-049f-4b8e-ba02-0272ba4f0aea" | |
}, | |
"source": [ | |
"%set_env DATA_DIR=/content/data\n", | |
"%set_env TF_RECORD_DIR=/content/data/tf_record" | |
], | |
"execution_count": 11, | |
"outputs": [ | |
{ | |
"output_type": "stream", | |
"text": [ | |
"env: DATA_DIR=/content/data\n", | |
"env: TF_RECORD_DIR=/content/data/tf_record\n" | |
], | |
"name": "stdout" | |
} | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"metadata": { | |
"id": "8ANpO7QmHjbo", | |
"colab": { | |
"base_uri": "https://localhost:8080/" | |
}, | |
"outputId": "defe6d85-c385-4879-8f86-c706085b11eb" | |
}, | |
"source": [ | |
"# Download dataset and extrac tar files.\n", | |
"!mkdir ${DATA_DIR}\n", | |
"!mkdir ${TF_RECORD_DIR}\n", | |
"!wget http://www.robots.ox.ac.uk/~vgg/data/pets/data/images.tar.gz -P ${DATA_DIR}\n", | |
"!wget http://www.robots.ox.ac.uk/~vgg/data/pets/data/annotations.tar.gz -P ${DATA_DIR}\n", | |
"!tar xf ${DATA_DIR}/images.tar.gz -C ${DATA_DIR}\n", | |
"!tar xf ${DATA_DIR}/annotations.tar.gz -C ${DATA_DIR}\n", | |
"\n", | |
"# Create TF-Record.\n", | |
"!python object_detection/dataset_tools/create_pet_tf_record.py \\\n", | |
" --label_map_path=./object_detection/data/pet_label_map.pbtxt \\\n", | |
" --data_dir=\"${DATA_DIR}\" \\\n", | |
" --output_dir=\"${TF_RECORD_DIR}\"\n", | |
"\n", | |
"!ls ${TF_RECORD_DIR}" | |
], | |
"execution_count": 12, | |
"outputs": [ | |
{ | |
"output_type": "stream", | |
"text": [ | |
"--2021-05-03 22:43:57-- http://www.robots.ox.ac.uk/~vgg/data/pets/data/images.tar.gz\n", | |
"Resolving www.robots.ox.ac.uk (www.robots.ox.ac.uk)... 129.67.94.2\n", | |
"Connecting to www.robots.ox.ac.uk (www.robots.ox.ac.uk)|129.67.94.2|:80... connected.\n", | |
"HTTP request sent, awaiting response... 301 Moved Permanently\n", | |
"Location: https://www.robots.ox.ac.uk/~vgg/data/pets/data/images.tar.gz [following]\n", | |
"--2021-05-03 22:43:57-- https://www.robots.ox.ac.uk/~vgg/data/pets/data/images.tar.gz\n", | |
"Connecting to www.robots.ox.ac.uk (www.robots.ox.ac.uk)|129.67.94.2|:443... connected.\n", | |
"HTTP request sent, awaiting response... 200 OK\n", | |
"Length: 791918971 (755M) [application/x-gzip]\n", | |
"Saving to: ‘/content/data/images.tar.gz’\n", | |
"\n", | |
"images.tar.gz 100%[===================>] 755.23M 68.9MB/s in 11s \n", | |
"\n", | |
"2021-05-03 22:44:08 (68.1 MB/s) - ‘/content/data/images.tar.gz’ saved [791918971/791918971]\n", | |
"\n", | |
"--2021-05-03 22:44:08-- http://www.robots.ox.ac.uk/~vgg/data/pets/data/annotations.tar.gz\n", | |
"Resolving www.robots.ox.ac.uk (www.robots.ox.ac.uk)... 129.67.94.2\n", | |
"Connecting to www.robots.ox.ac.uk (www.robots.ox.ac.uk)|129.67.94.2|:80... connected.\n", | |
"HTTP request sent, awaiting response... 301 Moved Permanently\n", | |
"Location: https://www.robots.ox.ac.uk/~vgg/data/pets/data/annotations.tar.gz [following]\n", | |
"--2021-05-03 22:44:08-- https://www.robots.ox.ac.uk/~vgg/data/pets/data/annotations.tar.gz\n", | |
"Connecting to www.robots.ox.ac.uk (www.robots.ox.ac.uk)|129.67.94.2|:443... connected.\n", | |
"HTTP request sent, awaiting response... 200 OK\n", | |
"Length: 19173078 (18M) [application/x-gzip]\n", | |
"Saving to: ‘/content/data/annotations.tar.gz’\n", | |
"\n", | |
"annotations.tar.gz 100%[===================>] 18.28M 58.9MB/s in 0.3s \n", | |
"\n", | |
"2021-05-03 22:44:09 (58.9 MB/s) - ‘/content/data/annotations.tar.gz’ saved [19173078/19173078]\n", | |
"\n", | |
"I0503 22:44:17.412478 139960508098432 create_pet_tf_record.py:273] Reading from Pet dataset.\n", | |
"I0503 22:44:17.422560 139960508098432 create_pet_tf_record.py:288] 2576 training and 1104 validation examples.\n", | |
"I0503 22:44:17.423127 139960508098432 create_pet_tf_record.py:241] On image 0 of 2576\n", | |
"/usr/local/lib/python3.7/dist-packages/object_detection/utils/dataset_util.py:83: FutureWarning: The behavior of this method will change in future versions. Use specific 'len(elem)' or 'elem is not None' test instead.\n", | |
" if not xml:\n", | |
"I0503 22:44:17.630365 139960508098432 create_pet_tf_record.py:241] On image 100 of 2576\n", | |
"I0503 22:44:17.820542 139960508098432 create_pet_tf_record.py:241] On image 200 of 2576\n", | |
"I0503 22:44:18.008630 139960508098432 create_pet_tf_record.py:241] On image 300 of 2576\n", | |
"I0503 22:44:18.212171 139960508098432 create_pet_tf_record.py:241] On image 400 of 2576\n", | |
"I0503 22:44:18.400474 139960508098432 create_pet_tf_record.py:241] On image 500 of 2576\n", | |
"W0503 22:44:18.538891 139960508098432 create_pet_tf_record.py:246] Could not find /content/data/annotations/xmls/Egyptian_Mau_14.xml, ignoring example.\n", | |
"W0503 22:44:18.570832 139960508098432 create_pet_tf_record.py:246] Could not find /content/data/annotations/xmls/saint_bernard_15.xml, ignoring example.\n", | |
"I0503 22:44:18.592516 139960508098432 create_pet_tf_record.py:241] On image 600 of 2576\n", | |
"W0503 22:44:18.600499 139960508098432 create_pet_tf_record.py:246] Could not find /content/data/annotations/xmls/Ragdoll_199.xml, ignoring example.\n", | |
"I0503 22:44:18.784520 139960508098432 create_pet_tf_record.py:241] On image 700 of 2576\n", | |
"I0503 22:44:18.980668 139960508098432 create_pet_tf_record.py:241] On image 800 of 2576\n", | |
"I0503 22:44:19.164678 139960508098432 create_pet_tf_record.py:241] On image 900 of 2576\n", | |
"I0503 22:44:19.353859 139960508098432 create_pet_tf_record.py:241] On image 1000 of 2576\n", | |
"I0503 22:44:19.542048 139960508098432 create_pet_tf_record.py:241] On image 1100 of 2576\n", | |
"I0503 22:44:19.754319 139960508098432 create_pet_tf_record.py:241] On image 1200 of 2576\n", | |
"I0503 22:44:19.959057 139960508098432 create_pet_tf_record.py:241] On image 1300 of 2576\n", | |
"I0503 22:44:20.199476 139960508098432 create_pet_tf_record.py:241] On image 1400 of 2576\n", | |
"W0503 22:44:20.300813 139960508098432 create_pet_tf_record.py:246] Could not find /content/data/annotations/xmls/Egyptian_Mau_186.xml, ignoring example.\n", | |
"I0503 22:44:20.392260 139960508098432 create_pet_tf_record.py:241] On image 1500 of 2576\n", | |
"W0503 22:44:20.521817 139960508098432 create_pet_tf_record.py:246] Could not find /content/data/annotations/xmls/Bengal_175.xml, ignoring example.\n", | |
"I0503 22:44:20.583704 139960508098432 create_pet_tf_record.py:241] On image 1600 of 2576\n", | |
"I0503 22:44:20.766173 139960508098432 create_pet_tf_record.py:241] On image 1700 of 2576\n", | |
"W0503 22:44:20.880429 139960508098432 create_pet_tf_record.py:246] Could not find /content/data/annotations/xmls/samoyed_10.xml, ignoring example.\n", | |
"I0503 22:44:20.946202 139960508098432 create_pet_tf_record.py:241] On image 1800 of 2576\n", | |
"I0503 22:44:21.145046 139960508098432 create_pet_tf_record.py:241] On image 1900 of 2576\n", | |
"I0503 22:44:21.332493 139960508098432 create_pet_tf_record.py:241] On image 2000 of 2576\n", | |
"I0503 22:44:21.528210 139960508098432 create_pet_tf_record.py:241] On image 2100 of 2576\n", | |
"W0503 22:44:21.596982 139960508098432 create_pet_tf_record.py:246] Could not find /content/data/annotations/xmls/Egyptian_Mau_156.xml, ignoring example.\n", | |
"I0503 22:44:21.728883 139960508098432 create_pet_tf_record.py:241] On image 2200 of 2576\n", | |
"I0503 22:44:21.926898 139960508098432 create_pet_tf_record.py:241] On image 2300 of 2576\n", | |
"I0503 22:44:22.108330 139960508098432 create_pet_tf_record.py:241] On image 2400 of 2576\n", | |
"I0503 22:44:22.298947 139960508098432 create_pet_tf_record.py:241] On image 2500 of 2576\n", | |
"W0503 22:44:22.395591 139960508098432 create_pet_tf_record.py:246] Could not find /content/data/annotations/xmls/Abyssinian_104.xml, ignoring example.\n", | |
"I0503 22:44:22.450354 139960508098432 create_pet_tf_record.py:241] On image 0 of 1104\n", | |
"I0503 22:44:22.641016 139960508098432 create_pet_tf_record.py:241] On image 100 of 1104\n", | |
"I0503 22:44:22.834721 139960508098432 create_pet_tf_record.py:241] On image 200 of 1104\n", | |
"I0503 22:44:23.016927 139960508098432 create_pet_tf_record.py:241] On image 300 of 1104\n", | |
"I0503 22:44:23.217114 139960508098432 create_pet_tf_record.py:241] On image 400 of 1104\n", | |
"I0503 22:44:23.400382 139960508098432 create_pet_tf_record.py:241] On image 500 of 1104\n", | |
"I0503 22:44:23.596589 139960508098432 create_pet_tf_record.py:241] On image 600 of 1104\n", | |
"I0503 22:44:23.786553 139960508098432 create_pet_tf_record.py:241] On image 700 of 1104\n", | |
"I0503 22:44:23.993257 139960508098432 create_pet_tf_record.py:241] On image 800 of 1104\n", | |
"W0503 22:44:24.078453 139960508098432 create_pet_tf_record.py:246] Could not find /content/data/annotations/xmls/Bengal_111.xml, ignoring example.\n", | |
"I0503 22:44:24.195823 139960508098432 create_pet_tf_record.py:241] On image 900 of 1104\n", | |
"I0503 22:44:24.379019 139960508098432 create_pet_tf_record.py:241] On image 1000 of 1104\n", | |
"I0503 22:44:24.605327 139960508098432 create_pet_tf_record.py:241] On image 1100 of 1104\n", | |
"pet_faces_train.record-00000-of-00010 pet_faces_val.record-00000-of-00010\n", | |
"pet_faces_train.record-00001-of-00010 pet_faces_val.record-00001-of-00010\n", | |
"pet_faces_train.record-00002-of-00010 pet_faces_val.record-00002-of-00010\n", | |
"pet_faces_train.record-00003-of-00010 pet_faces_val.record-00003-of-00010\n", | |
"pet_faces_train.record-00004-of-00010 pet_faces_val.record-00004-of-00010\n", | |
"pet_faces_train.record-00005-of-00010 pet_faces_val.record-00005-of-00010\n", | |
"pet_faces_train.record-00006-of-00010 pet_faces_val.record-00006-of-00010\n", | |
"pet_faces_train.record-00007-of-00010 pet_faces_val.record-00007-of-00010\n", | |
"pet_faces_train.record-00008-of-00010 pet_faces_val.record-00008-of-00010\n", | |
"pet_faces_train.record-00009-of-00010 pet_faces_val.record-00009-of-00010\n" | |
], | |
"name": "stdout" | |
} | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"id": "Ndb6N895JwJ6" | |
}, | |
"source": [ | |
"# Get the pretrained model for transfer learning\n", | |
"\n" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"metadata": { | |
"id": "at-wzrEXkDIe", | |
"colab": { | |
"base_uri": "https://localhost:8080/" | |
}, | |
"outputId": "d3a1fb15-ede1-4240-b36e-bfbe1107bef1" | |
}, | |
"source": [ | |
"!wget http://download.tensorflow.org/models/object_detection/ssd_mobilenet_v3_large_coco_2020_01_14.tar.gz -P ${DATA_DIR}\n", | |
"!tar xf ${DATA_DIR}/ssd_mobilenet_v3_large_coco_2020_01_14.tar.gz -C ${DATA_DIR}" | |
], | |
"execution_count": 13, | |
"outputs": [ | |
{ | |
"output_type": "stream", | |
"text": [ | |
"--2021-05-03 22:44:25-- http://download.tensorflow.org/models/object_detection/ssd_mobilenet_v3_large_coco_2020_01_14.tar.gz\n", | |
"Resolving download.tensorflow.org (download.tensorflow.org)... 173.194.79.128, 2a00:1450:4013:c00::80\n", | |
"Connecting to download.tensorflow.org (download.tensorflow.org)|173.194.79.128|:80... connected.\n", | |
"HTTP request sent, awaiting response... 200 OK\n", | |
"Length: 49318766 (47M) [application/gzip]\n", | |
"Saving to: ‘/content/data/ssd_mobilenet_v3_large_coco_2020_01_14.tar.gz’\n", | |
"\n", | |
"ssd_mobilenet_v3_la 100%[===================>] 47.03M 31.6MB/s in 1.5s \n", | |
"\n", | |
"2021-05-03 22:44:27 (31.6 MB/s) - ‘/content/data/ssd_mobilenet_v3_large_coco_2020_01_14.tar.gz’ saved [49318766/49318766]\n", | |
"\n" | |
], | |
"name": "stdout" | |
} | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"id": "KlCKFYLmBvI6" | |
}, | |
"source": [ | |
"# Create config file" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"metadata": { | |
"id": "pDwELNPC-Sqh" | |
}, | |
"source": [ | |
"!cp ./object_detection/samples/configs/ssdlite_mobilenet_v3_large_320x320_coco.config ${DATA_DIR}/ssdlite_mobilenet_v3_large_320x320_pet.config" | |
], | |
"execution_count": 14, | |
"outputs": [] | |
}, | |
{ | |
"cell_type": "code", | |
"metadata": { | |
"id": "wpRIcKWn_NnN" | |
}, | |
"source": [ | |
"%%bash\n", | |
"\n", | |
"cat <<EOF > ${DATA_DIR}/config.patch\n", | |
"--- ./object_detection/samples/configs/ssdlite_mobilenet_v3_large_320x320_coco.config\t2021-05-03 08:34:03.934459490 +0000\n", | |
"+++ /content/data/ssdlite_mobilenet_v3_large_320x320_pet.config\t2021-05-03 09:01:56.235655642 +0000\n", | |
"@@ -11,7 +11,7 @@\n", | |
" ssd {\n", | |
" inplace_batchnorm_update: true\n", | |
" freeze_batchnorm: false\n", | |
"- num_classes: 90\n", | |
"+ num_classes: 37\n", | |
" box_coder {\n", | |
" faster_rcnn_box_coder {\n", | |
" y_scale: 10.0\n", | |
"@@ -147,11 +147,14 @@\n", | |
" }\n", | |
" \n", | |
" train_config: {\n", | |
"- batch_size: 512\n", | |
"+ batch_size: 128\n", | |
" sync_replicas: true\n", | |
" startup_delay_steps: 0\n", | |
" replicas_to_aggregate: 32\n", | |
" num_steps: 400000\n", | |
"+ fine_tune_checkpoint: \"/content/data/ssd_mobilenet_v3_large_coco_2020_01_14/model.ckpt\"\n", | |
"+ load_all_detection_checkpoint_vars: true\n", | |
"+ fine_tune_checkpoint_type: \"detection\"\n", | |
" data_augmentation_options {\n", | |
" random_horizontal_flip {\n", | |
" }\n", | |
"@@ -164,9 +167,9 @@\n", | |
" momentum_optimizer: {\n", | |
" learning_rate: {\n", | |
" cosine_decay_learning_rate {\n", | |
"- learning_rate_base: 0.4\n", | |
"+ learning_rate_base: 0.04\n", | |
" total_steps: 400000\n", | |
"- warmup_learning_rate: 0.13333\n", | |
"+ warmup_learning_rate: 0.013333\n", | |
" warmup_steps: 2000\n", | |
" }\n", | |
" }\n", | |
"@@ -180,20 +183,20 @@\n", | |
" \n", | |
" train_input_reader: {\n", | |
" tf_record_input_reader {\n", | |
"- input_path: \"PATH_TO_BE_CONFIGURED/mscoco_train.record-?????-of-00100\"\n", | |
"+ input_path: \"/content/data/tf_record/pet_faces_train.record-?????-of-00010\"\n", | |
" }\n", | |
"- label_map_path: \"PATH_TO_BE_CONFIGURED/mscoco_label_map.pbtxt\"\n", | |
"+ label_map_path: \"/content/models/research/object_detection/data/pet_label_map.pbtxt\"\n", | |
" }\n", | |
" \n", | |
" eval_config: {\n", | |
"- num_examples: 8000\n", | |
"+ num_examples: 1104\n", | |
" }\n", | |
" \n", | |
" eval_input_reader: {\n", | |
" tf_record_input_reader {\n", | |
"- input_path: \"PATH_TO_BE_CONFIGURED/mscoco_val.record-?????-of-00010\"\n", | |
"+ input_path: \"/content/data/tf_record/pet_faces_train.record-?????-of-00010\"\n", | |
" }\n", | |
"- label_map_path: \"PATH_TO_BE_CONFIGURED/mscoco_label_map.pbtxt\"\n", | |
"+ label_map_path: \"/content/models/research/object_detection/data/pet_label_map.pbtxt\"\n", | |
" shuffle: false\n", | |
" num_readers: 1\n", | |
" }\n", | |
"EOF" | |
], | |
"execution_count": 15, | |
"outputs": [] | |
}, | |
{ | |
"cell_type": "code", | |
"metadata": { | |
"colab": { | |
"base_uri": "https://localhost:8080/" | |
}, | |
"id": "kmptYYT6-PyE", | |
"outputId": "45ee3bbe-4286-4e0d-bfb5-1b58b18a09fb" | |
}, | |
"source": [ | |
"!patch -u ${DATA_DIR}/ssdlite_mobilenet_v3_large_320x320_pet.config < ${DATA_DIR}/config.patch" | |
], | |
"execution_count": 16, | |
"outputs": [ | |
{ | |
"output_type": "stream", | |
"text": [ | |
"patching file /content/data/ssdlite_mobilenet_v3_large_320x320_pet.config\n" | |
], | |
"name": "stdout" | |
} | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"metadata": { | |
"id": "SwbAVryqA5t7", | |
"colab": { | |
"base_uri": "https://localhost:8080/" | |
}, | |
"outputId": "3f75d300-d25f-4bf8-d26a-cb9eccf9d6b6" | |
}, | |
"source": [ | |
"!cat ${DATA_DIR}/ssdlite_mobilenet_v3_large_320x320_pet.config" | |
], | |
"execution_count": 17, | |
"outputs": [ | |
{ | |
"output_type": "stream", | |
"text": [ | |
"# SSDLite with Mobilenet v3 large feature extractor.\n", | |
"# Trained on COCO14, initialized from scratch.\n", | |
"# 3.22M parameters, 1.02B FLOPs\n", | |
"# TPU-compatible.\n", | |
"# Users should configure the fine_tune_checkpoint field in the train config as\n", | |
"# well as the label_map_path and input_path fields in the train_input_reader and\n", | |
"# eval_input_reader. Search for \"PATH_TO_BE_CONFIGURED\" to find the fields that\n", | |
"# should be configured.\n", | |
"\n", | |
"model {\n", | |
" ssd {\n", | |
" inplace_batchnorm_update: true\n", | |
" freeze_batchnorm: false\n", | |
" num_classes: 37\n", | |
" box_coder {\n", | |
" faster_rcnn_box_coder {\n", | |
" y_scale: 10.0\n", | |
" x_scale: 10.0\n", | |
" height_scale: 5.0\n", | |
" width_scale: 5.0\n", | |
" }\n", | |
" }\n", | |
" matcher {\n", | |
" argmax_matcher {\n", | |
" matched_threshold: 0.5\n", | |
" unmatched_threshold: 0.5\n", | |
" ignore_thresholds: false\n", | |
" negatives_lower_than_unmatched: true\n", | |
" force_match_for_each_row: true\n", | |
" use_matmul_gather: true\n", | |
" }\n", | |
" }\n", | |
" similarity_calculator {\n", | |
" iou_similarity {\n", | |
" }\n", | |
" }\n", | |
" encode_background_as_zeros: true\n", | |
" anchor_generator {\n", | |
" ssd_anchor_generator {\n", | |
" num_layers: 6\n", | |
" min_scale: 0.2\n", | |
" max_scale: 0.95\n", | |
" aspect_ratios: 1.0\n", | |
" aspect_ratios: 2.0\n", | |
" aspect_ratios: 0.5\n", | |
" aspect_ratios: 3.0\n", | |
" aspect_ratios: 0.3333\n", | |
" }\n", | |
" }\n", | |
" image_resizer {\n", | |
" fixed_shape_resizer {\n", | |
" height: 320\n", | |
" width: 320\n", | |
" }\n", | |
" }\n", | |
" box_predictor {\n", | |
" convolutional_box_predictor {\n", | |
" min_depth: 0\n", | |
" max_depth: 0\n", | |
" num_layers_before_predictor: 0\n", | |
" use_dropout: false\n", | |
" dropout_keep_probability: 0.8\n", | |
" kernel_size: 3\n", | |
" use_depthwise: true\n", | |
" box_code_size: 4\n", | |
" apply_sigmoid_to_scores: false\n", | |
" class_prediction_bias_init: -4.6\n", | |
" conv_hyperparams {\n", | |
" activation: RELU_6,\n", | |
" regularizer {\n", | |
" l2_regularizer {\n", | |
" weight: 0.00004\n", | |
" }\n", | |
" }\n", | |
" initializer {\n", | |
" random_normal_initializer {\n", | |
" stddev: 0.03\n", | |
" mean: 0.0\n", | |
" }\n", | |
" }\n", | |
" batch_norm {\n", | |
" train: true,\n", | |
" scale: true,\n", | |
" center: true,\n", | |
" decay: 0.97,\n", | |
" epsilon: 0.001,\n", | |
" }\n", | |
" }\n", | |
" }\n", | |
" }\n", | |
" feature_extractor {\n", | |
" type: 'ssd_mobilenet_v3_large'\n", | |
" min_depth: 16\n", | |
" depth_multiplier: 1.0\n", | |
" use_depthwise: true\n", | |
" conv_hyperparams {\n", | |
" activation: RELU_6,\n", | |
" regularizer {\n", | |
" l2_regularizer {\n", | |
" weight: 0.00004\n", | |
" }\n", | |
" }\n", | |
" initializer {\n", | |
" truncated_normal_initializer {\n", | |
" stddev: 0.03\n", | |
" mean: 0.0\n", | |
" }\n", | |
" }\n", | |
" batch_norm {\n", | |
" train: true,\n", | |
" scale: true,\n", | |
" center: true,\n", | |
" decay: 0.97,\n", | |
" epsilon: 0.001,\n", | |
" }\n", | |
" }\n", | |
" override_base_feature_extractor_hyperparams: true\n", | |
" }\n", | |
" loss {\n", | |
" classification_loss {\n", | |
" weighted_sigmoid_focal {\n", | |
" alpha: 0.75,\n", | |
" gamma: 2.0\n", | |
" }\n", | |
" }\n", | |
" localization_loss {\n", | |
" weighted_smooth_l1 {\n", | |
" delta: 1.0\n", | |
" }\n", | |
" }\n", | |
" classification_weight: 1.0\n", | |
" localization_weight: 1.0\n", | |
" }\n", | |
" normalize_loss_by_num_matches: true\n", | |
" normalize_loc_loss_by_codesize: true\n", | |
" post_processing {\n", | |
" batch_non_max_suppression {\n", | |
" score_threshold: 1e-8\n", | |
" iou_threshold: 0.6\n", | |
" max_detections_per_class: 100\n", | |
" max_total_detections: 100\n", | |
" use_static_shapes: true\n", | |
" }\n", | |
" score_converter: SIGMOID\n", | |
" }\n", | |
" }\n", | |
"}\n", | |
"\n", | |
"train_config: {\n", | |
" batch_size: 128\n", | |
" sync_replicas: true\n", | |
" startup_delay_steps: 0\n", | |
" replicas_to_aggregate: 32\n", | |
" num_steps: 400000\n", | |
" fine_tune_checkpoint: \"/content/data/ssd_mobilenet_v3_large_coco_2020_01_14/model.ckpt\"\n", | |
" load_all_detection_checkpoint_vars: true\n", | |
" fine_tune_checkpoint_type: \"detection\"\n", | |
" data_augmentation_options {\n", | |
" random_horizontal_flip {\n", | |
" }\n", | |
" }\n", | |
" data_augmentation_options {\n", | |
" ssd_random_crop {\n", | |
" }\n", | |
" }\n", | |
" optimizer {\n", | |
" momentum_optimizer: {\n", | |
" learning_rate: {\n", | |
" cosine_decay_learning_rate {\n", | |
" learning_rate_base: 0.04\n", | |
" total_steps: 400000\n", | |
" warmup_learning_rate: 0.013333\n", | |
" warmup_steps: 2000\n", | |
" }\n", | |
" }\n", | |
" momentum_optimizer_value: 0.9\n", | |
" }\n", | |
" use_moving_average: false\n", | |
" }\n", | |
" max_number_of_boxes: 100\n", | |
" unpad_groundtruth_tensors: false\n", | |
"}\n", | |
"\n", | |
"train_input_reader: {\n", | |
" tf_record_input_reader {\n", | |
" input_path: \"/content/data/tf_record/pet_faces_train.record-?????-of-00010\"\n", | |
" }\n", | |
" label_map_path: \"/content/models/research/object_detection/data/pet_label_map.pbtxt\"\n", | |
"}\n", | |
"\n", | |
"eval_config: {\n", | |
" num_examples: 1104\n", | |
"}\n", | |
"\n", | |
"eval_input_reader: {\n", | |
" tf_record_input_reader {\n", | |
" input_path: \"/content/data/tf_record/pet_faces_train.record-?????-of-00010\"\n", | |
" }\n", | |
" label_map_path: \"/content/models/research/object_detection/data/pet_label_map.pbtxt\"\n", | |
" shuffle: false\n", | |
" num_readers: 1\n", | |
"}\n", | |
"\n" | |
], | |
"name": "stdout" | |
} | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"id": "rF7J2C_aUb0n" | |
}, | |
"source": [ | |
"# Train model" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"metadata": { | |
"id": "gs57oAxVUery", | |
"colab": { | |
"base_uri": "https://localhost:8080/" | |
}, | |
"outputId": "d630b65c-d947-4fcd-dd20-849914a53466" | |
}, | |
"source": [ | |
"!python ./object_detection/model_main.py \\\n", | |
" --alsologtostderr \\\n", | |
" --pipeline_config_path=${DATA_DIR}/ssdlite_mobilenet_v3_large_320x320_pet.config \\\n", | |
" --num_train_steps=5000 \\\n", | |
" --sample_1_of_n_eval_examples=1 \\\n", | |
" --model_dir=${DATA_DIR}/train_ssdlite_mobilenet_v3_large_320x320_pet" | |
], | |
"execution_count": 18, | |
"outputs": [ | |
{ | |
"output_type": "stream", | |
"text": [ | |
"WARNING:tensorflow:Forced number of epochs for all eval validations to be 1.\n", | |
"W0503 22:44:31.944674 140215059400576 model_lib.py:820] Forced number of epochs for all eval validations to be 1.\n", | |
"INFO:tensorflow:Maybe overwriting train_steps: 5000\n", | |
"I0503 22:44:31.944860 140215059400576 config_util.py:552] Maybe overwriting train_steps: 5000\n", | |
"INFO:tensorflow:Maybe overwriting use_bfloat16: False\n", | |
"I0503 22:44:31.944965 140215059400576 config_util.py:552] Maybe overwriting use_bfloat16: False\n", | |
"INFO:tensorflow:Maybe overwriting sample_1_of_n_eval_examples: 1\n", | |
"I0503 22:44:31.945029 140215059400576 config_util.py:552] Maybe overwriting sample_1_of_n_eval_examples: 1\n", | |
"INFO:tensorflow:Maybe overwriting eval_num_epochs: 1\n", | |
"I0503 22:44:31.945102 140215059400576 config_util.py:552] Maybe overwriting eval_num_epochs: 1\n", | |
"WARNING:tensorflow:Expected number of evaluation epochs is 1, but instead encountered `eval_on_train_input_config.num_epochs` = 0. Overwriting `num_epochs` to 1.\n", | |
"W0503 22:44:31.945193 140215059400576 model_lib.py:836] Expected number of evaluation epochs is 1, but instead encountered `eval_on_train_input_config.num_epochs` = 0. Overwriting `num_epochs` to 1.\n", | |
"INFO:tensorflow:create_estimator_and_inputs: use_tpu False, export_to_tpu None\n", | |
"I0503 22:44:31.945266 140215059400576 model_lib.py:873] create_estimator_and_inputs: use_tpu False, export_to_tpu None\n", | |
"INFO:tensorflow:Using config: {'_model_dir': '/content/data/train_ssdlite_mobilenet_v3_large_320x320_pet', '_tf_random_seed': None, '_save_summary_steps': 100, '_save_checkpoints_steps': None, '_save_checkpoints_secs': 600, '_session_config': allow_soft_placement: true\n", | |
"graph_options {\n", | |
" rewrite_options {\n", | |
" meta_optimizer_iterations: ONE\n", | |
" }\n", | |
"}\n", | |
", '_keep_checkpoint_max': 5, '_keep_checkpoint_every_n_hours': 10000, '_log_step_count_steps': 100, '_train_distribute': None, '_device_fn': None, '_protocol': None, '_eval_distribute': None, '_experimental_distribute': None, '_experimental_max_worker_delay_secs': None, '_session_creation_timeout_secs': 7200, '_service': None, '_cluster_spec': <tensorflow.python.training.server_lib.ClusterSpec object at 0x7f85c7012d90>, '_task_type': 'worker', '_task_id': 0, '_global_id_in_cluster': 0, '_master': '', '_evaluation_master': '', '_is_chief': True, '_num_ps_replicas': 0, '_num_worker_replicas': 1}\n", | |
"I0503 22:44:31.945604 140215059400576 estimator.py:212] Using config: {'_model_dir': '/content/data/train_ssdlite_mobilenet_v3_large_320x320_pet', '_tf_random_seed': None, '_save_summary_steps': 100, '_save_checkpoints_steps': None, '_save_checkpoints_secs': 600, '_session_config': allow_soft_placement: true\n", | |
"graph_options {\n", | |
" rewrite_options {\n", | |
" meta_optimizer_iterations: ONE\n", | |
" }\n", | |
"}\n", | |
", '_keep_checkpoint_max': 5, '_keep_checkpoint_every_n_hours': 10000, '_log_step_count_steps': 100, '_train_distribute': None, '_device_fn': None, '_protocol': None, '_eval_distribute': None, '_experimental_distribute': None, '_experimental_max_worker_delay_secs': None, '_session_creation_timeout_secs': 7200, '_service': None, '_cluster_spec': <tensorflow.python.training.server_lib.ClusterSpec object at 0x7f85c7012d90>, '_task_type': 'worker', '_task_id': 0, '_global_id_in_cluster': 0, '_master': '', '_evaluation_master': '', '_is_chief': True, '_num_ps_replicas': 0, '_num_worker_replicas': 1}\n", | |
"WARNING:tensorflow:Estimator's model_fn (<function create_model_fn.<locals>.model_fn at 0x7f85c7013a70>) includes params argument, but params are not passed to Estimator.\n", | |
"W0503 22:44:31.945781 140215059400576 model_fn.py:630] Estimator's model_fn (<function create_model_fn.<locals>.model_fn at 0x7f85c7013a70>) includes params argument, but params are not passed to Estimator.\n", | |
"INFO:tensorflow:Not using Distribute Coordinator.\n", | |
"I0503 22:44:31.946280 140215059400576 estimator_training.py:186] Not using Distribute Coordinator.\n", | |
"INFO:tensorflow:Running training and evaluation locally (non-distributed).\n", | |
"I0503 22:44:31.946429 140215059400576 training.py:612] Running training and evaluation locally (non-distributed).\n", | |
"INFO:tensorflow:Start train and evaluate loop. The evaluate will happen after every checkpoint. Checkpoint frequency is determined based on RunConfig arguments: save_checkpoints_steps None or save_checkpoints_secs 600.\n", | |
"I0503 22:44:31.946604 140215059400576 training.py:700] Start train and evaluate loop. The evaluate will happen after every checkpoint. Checkpoint frequency is determined based on RunConfig arguments: save_checkpoints_steps None or save_checkpoints_secs 600.\n", | |
"WARNING:tensorflow:From /tensorflow-1.15.2/python3.7/tensorflow_core/python/training/training_util.py:236: Variable.initialized_value (from tensorflow.python.ops.variables) is deprecated and will be removed in a future version.\n", | |
"Instructions for updating:\n", | |
"Use Variable.read_value. Variables in 2.X are initialized automatically both in eager and graph (inside tf.defun) contexts.\n", | |
"W0503 22:44:31.950980 140215059400576 deprecation.py:323] From /tensorflow-1.15.2/python3.7/tensorflow_core/python/training/training_util.py:236: Variable.initialized_value (from tensorflow.python.ops.variables) is deprecated and will be removed in a future version.\n", | |
"Instructions for updating:\n", | |
"Use Variable.read_value. Variables in 2.X are initialized automatically both in eager and graph (inside tf.defun) contexts.\n", | |
"INFO:tensorflow:Reading unweighted datasets: ['/content/data/tf_record/pet_faces_train.record-?????-of-00010']\n", | |
"I0503 22:44:31.978471 140215059400576 dataset_builder.py:163] Reading unweighted datasets: ['/content/data/tf_record/pet_faces_train.record-?????-of-00010']\n", | |
"INFO:tensorflow:Reading record datasets for input file: ['/content/data/tf_record/pet_faces_train.record-?????-of-00010']\n", | |
"I0503 22:44:31.979441 140215059400576 dataset_builder.py:80] Reading record datasets for input file: ['/content/data/tf_record/pet_faces_train.record-?????-of-00010']\n", | |
"INFO:tensorflow:Number of filenames to read: 10\n", | |
"I0503 22:44:31.979539 140215059400576 dataset_builder.py:81] Number of filenames to read: 10\n", | |
"WARNING:tensorflow:num_readers has been reduced to 10 to match input file shards.\n", | |
"W0503 22:44:31.979594 140215059400576 dataset_builder.py:88] num_readers has been reduced to 10 to match input file shards.\n", | |
"WARNING:tensorflow:From /usr/local/lib/python3.7/dist-packages/object_detection/builders/dataset_builder.py:105: parallel_interleave (from tensorflow.python.data.experimental.ops.interleave_ops) is deprecated and will be removed in a future version.\n", | |
"Instructions for updating:\n", | |
"Use `tf.data.Dataset.interleave(map_func, cycle_length, block_length, num_parallel_calls=tf.data.experimental.AUTOTUNE)` instead. If sloppy execution is desired, use `tf.data.Options.experimental_determinstic`.\n", | |
"W0503 22:44:31.984447 140215059400576 deprecation.py:323] From /usr/local/lib/python3.7/dist-packages/object_detection/builders/dataset_builder.py:105: parallel_interleave (from tensorflow.python.data.experimental.ops.interleave_ops) is deprecated and will be removed in a future version.\n", | |
"Instructions for updating:\n", | |
"Use `tf.data.Dataset.interleave(map_func, cycle_length, block_length, num_parallel_calls=tf.data.experimental.AUTOTUNE)` instead. If sloppy execution is desired, use `tf.data.Options.experimental_determinstic`.\n", | |
"WARNING:tensorflow:From /usr/local/lib/python3.7/dist-packages/object_detection/builders/dataset_builder.py:237: DatasetV1.map_with_legacy_function (from tensorflow.python.data.ops.dataset_ops) is deprecated and will be removed in a future version.\n", | |
"Instructions for updating:\n", | |
"Use `tf.data.Dataset.map()\n", | |
"W0503 22:44:32.002814 140215059400576 deprecation.py:323] From /usr/local/lib/python3.7/dist-packages/object_detection/builders/dataset_builder.py:237: DatasetV1.map_with_legacy_function (from tensorflow.python.data.ops.dataset_ops) is deprecated and will be removed in a future version.\n", | |
"Instructions for updating:\n", | |
"Use `tf.data.Dataset.map()\n", | |
"WARNING:tensorflow:Entity <bound method TfExampleDecoder.decode of <object_detection.data_decoders.tf_example_decoder.TfExampleDecoder object at 0x7f85c7027dd0>> could not be transformed and will be executed as-is. Please report this to the AutoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: module 'gast' has no attribute 'Num'\n", | |
"W0503 22:44:32.031176 140215059400576 ag_logging.py:146] Entity <bound method TfExampleDecoder.decode of <object_detection.data_decoders.tf_example_decoder.TfExampleDecoder object at 0x7f85c7027dd0>> could not be transformed and will be executed as-is. Please report this to the AutoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: module 'gast' has no attribute 'Num'\n", | |
"WARNING:tensorflow:Entity <function train_input.<locals>.transform_and_pad_input_data_fn at 0x7f85c7013e60> could not be transformed and will be executed as-is. Please report this to the AutoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: Bad argument number for Name: 3, expecting 4\n", | |
"W0503 22:44:32.213216 140215059400576 ag_logging.py:146] Entity <function train_input.<locals>.transform_and_pad_input_data_fn at 0x7f85c7013e60> could not be transformed and will be executed as-is. Please report this to the AutoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: Bad argument number for Name: 3, expecting 4\n", | |
"WARNING:tensorflow:From /usr/local/lib/python3.7/dist-packages/object_detection/inputs.py:111: where (from tensorflow.python.ops.array_ops) is deprecated and will be removed in a future version.\n", | |
"Instructions for updating:\n", | |
"Use tf.where in 2.0, which has the same broadcast rule as np.where\n", | |
"W0503 22:44:32.214441 140215059400576 deprecation.py:323] From /usr/local/lib/python3.7/dist-packages/object_detection/inputs.py:111: where (from tensorflow.python.ops.array_ops) is deprecated and will be removed in a future version.\n", | |
"Instructions for updating:\n", | |
"Use tf.where in 2.0, which has the same broadcast rule as np.where\n", | |
"WARNING:tensorflow:From /usr/local/lib/python3.7/dist-packages/object_detection/inputs.py:97: sparse_to_dense (from tensorflow.python.ops.sparse_ops) is deprecated and will be removed in a future version.\n", | |
"Instructions for updating:\n", | |
"Create a `tf.sparse.SparseTensor` and use `tf.sparse.to_dense` instead.\n", | |
"W0503 22:44:32.223712 140215059400576 deprecation.py:323] From /usr/local/lib/python3.7/dist-packages/object_detection/inputs.py:97: sparse_to_dense (from tensorflow.python.ops.sparse_ops) is deprecated and will be removed in a future version.\n", | |
"Instructions for updating:\n", | |
"Create a `tf.sparse.SparseTensor` and use `tf.sparse.to_dense` instead.\n", | |
"WARNING:tensorflow:From /usr/local/lib/python3.7/dist-packages/object_detection/core/preprocessor.py:199: sample_distorted_bounding_box (from tensorflow.python.ops.image_ops_impl) is deprecated and will be removed in a future version.\n", | |
"Instructions for updating:\n", | |
"`seed2` arg is deprecated.Use sample_distorted_bounding_box_v2 instead.\n", | |
"W0503 22:44:32.318740 140215059400576 deprecation.py:323] From /usr/local/lib/python3.7/dist-packages/object_detection/core/preprocessor.py:199: sample_distorted_bounding_box (from tensorflow.python.ops.image_ops_impl) is deprecated and will be removed in a future version.\n", | |
"Instructions for updating:\n", | |
"`seed2` arg is deprecated.Use sample_distorted_bounding_box_v2 instead.\n", | |
"WARNING:tensorflow:From /usr/local/lib/python3.7/dist-packages/object_detection/inputs.py:284: to_float (from tensorflow.python.ops.math_ops) is deprecated and will be removed in a future version.\n", | |
"Instructions for updating:\n", | |
"Use `tf.cast` instead.\n", | |
"W0503 22:44:32.987949 140215059400576 deprecation.py:323] From /usr/local/lib/python3.7/dist-packages/object_detection/inputs.py:284: to_float (from tensorflow.python.ops.math_ops) is deprecated and will be removed in a future version.\n", | |
"Instructions for updating:\n", | |
"Use `tf.cast` instead.\n", | |
"INFO:tensorflow:Calling model_fn.\n", | |
"I0503 22:44:33.405897 140215059400576 estimator.py:1148] Calling model_fn.\n", | |
"WARNING:tensorflow:From /usr/local/lib/python3.7/dist-packages/tf_slim/layers/layers.py:1089: Layer.apply (from tensorflow.python.keras.engine.base_layer) is deprecated and will be removed in a future version.\n", | |
"Instructions for updating:\n", | |
"Please use `layer.__call__` method instead.\n", | |
"W0503 22:44:33.543333 140215059400576 deprecation.py:323] From /usr/local/lib/python3.7/dist-packages/tf_slim/layers/layers.py:1089: Layer.apply (from tensorflow.python.keras.engine.base_layer) is deprecated and will be removed in a future version.\n", | |
"Instructions for updating:\n", | |
"Please use `layer.__call__` method instead.\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0503 22:44:35.805704 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0503 22:44:35.882828 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0503 22:44:35.962100 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0503 22:44:36.040409 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0503 22:44:36.118508 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0503 22:44:36.195902 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:fine tuning parameter: fine_tune_checkpoint_type detection, from_detection_checkpoint False fine_tune_checkpoint /content/data/ssd_mobilenet_v3_large_coco_2020_01_14/model.ckpt load_all_detection_checkpoint_vars True\n", | |
"I0503 22:44:36.389824 140215059400576 model_lib.py:517] fine tuning parameter: fine_tune_checkpoint_type detection, from_detection_checkpoint False fine_tune_checkpoint /content/data/ssd_mobilenet_v3_large_coco_2020_01_14/model.ckpt load_all_detection_checkpoint_vars True\n", | |
"W0503 22:44:36.397223 140215059400576 variables_helper.py:153] Variable [BoxPredictor_0/ClassPredictor/biases] is available in checkpoint, but has an incompatible shape with model variable. Checkpoint shape: [[273]], model variable shape: [[114]]. This variable will not be initialized from the checkpoint.\n", | |
"W0503 22:44:36.397351 140215059400576 variables_helper.py:153] Variable [BoxPredictor_0/ClassPredictor/weights] is available in checkpoint, but has an incompatible shape with model variable. Checkpoint shape: [[1, 1, 672, 273]], model variable shape: [[1, 1, 672, 114]]. This variable will not be initialized from the checkpoint.\n", | |
"W0503 22:44:36.397506 140215059400576 variables_helper.py:153] Variable [BoxPredictor_1/ClassPredictor/biases] is available in checkpoint, but has an incompatible shape with model variable. Checkpoint shape: [[546]], model variable shape: [[228]]. This variable will not be initialized from the checkpoint.\n", | |
"W0503 22:44:36.397562 140215059400576 variables_helper.py:153] Variable [BoxPredictor_1/ClassPredictor/weights] is available in checkpoint, but has an incompatible shape with model variable. Checkpoint shape: [[1, 1, 480, 546]], model variable shape: [[1, 1, 480, 228]]. This variable will not be initialized from the checkpoint.\n", | |
"W0503 22:44:36.397706 140215059400576 variables_helper.py:153] Variable [BoxPredictor_2/ClassPredictor/biases] is available in checkpoint, but has an incompatible shape with model variable. Checkpoint shape: [[546]], model variable shape: [[228]]. This variable will not be initialized from the checkpoint.\n", | |
"W0503 22:44:36.397794 140215059400576 variables_helper.py:153] Variable [BoxPredictor_2/ClassPredictor/weights] is available in checkpoint, but has an incompatible shape with model variable. Checkpoint shape: [[1, 1, 512, 546]], model variable shape: [[1, 1, 512, 228]]. This variable will not be initialized from the checkpoint.\n", | |
"W0503 22:44:36.397950 140215059400576 variables_helper.py:153] Variable [BoxPredictor_3/ClassPredictor/biases] is available in checkpoint, but has an incompatible shape with model variable. Checkpoint shape: [[546]], model variable shape: [[228]]. This variable will not be initialized from the checkpoint.\n", | |
"W0503 22:44:36.398005 140215059400576 variables_helper.py:153] Variable [BoxPredictor_3/ClassPredictor/weights] is available in checkpoint, but has an incompatible shape with model variable. Checkpoint shape: [[1, 1, 256, 546]], model variable shape: [[1, 1, 256, 228]]. This variable will not be initialized from the checkpoint.\n", | |
"W0503 22:44:36.398206 140215059400576 variables_helper.py:153] Variable [BoxPredictor_4/ClassPredictor/biases] is available in checkpoint, but has an incompatible shape with model variable. Checkpoint shape: [[546]], model variable shape: [[228]]. This variable will not be initialized from the checkpoint.\n", | |
"W0503 22:44:36.398305 140215059400576 variables_helper.py:153] Variable [BoxPredictor_4/ClassPredictor/weights] is available in checkpoint, but has an incompatible shape with model variable. Checkpoint shape: [[1, 1, 256, 546]], model variable shape: [[1, 1, 256, 228]]. This variable will not be initialized from the checkpoint.\n", | |
"W0503 22:44:36.398443 140215059400576 variables_helper.py:153] Variable [BoxPredictor_5/ClassPredictor/biases] is available in checkpoint, but has an incompatible shape with model variable. Checkpoint shape: [[546]], model variable shape: [[228]]. This variable will not be initialized from the checkpoint.\n", | |
"W0503 22:44:36.398492 140215059400576 variables_helper.py:153] Variable [BoxPredictor_5/ClassPredictor/weights] is available in checkpoint, but has an incompatible shape with model variable. Checkpoint shape: [[1, 1, 128, 546]], model variable shape: [[1, 1, 128, 228]]. This variable will not be initialized from the checkpoint.\n", | |
"W0503 22:44:36.400852 140215059400576 variables_helper.py:156] Variable [global_step] is not available in checkpoint\n", | |
"INFO:tensorflow:Done calling model_fn.\n", | |
"I0503 22:44:57.526339 140215059400576 estimator.py:1150] Done calling model_fn.\n", | |
"INFO:tensorflow:Create CheckpointSaverHook.\n", | |
"I0503 22:44:57.527533 140215059400576 basic_session_run_hooks.py:541] Create CheckpointSaverHook.\n", | |
"INFO:tensorflow:Graph was finalized.\n", | |
"I0503 22:45:02.287679 140215059400576 monitored_session.py:240] Graph was finalized.\n", | |
"2021-05-03 22:45:02.288169: I tensorflow/core/platform/cpu_feature_guard.cc:142] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX512F\n", | |
"2021-05-03 22:45:02.299423: I tensorflow/core/platform/profile_utils/cpu_utils.cc:94] CPU Frequency: 2000179999 Hz\n", | |
"2021-05-03 22:45:02.299789: I tensorflow/compiler/xla/service/service.cc:168] XLA service 0x555b93d53800 initialized for platform Host (this does not guarantee that XLA will be used). Devices:\n", | |
"2021-05-03 22:45:02.299819: I tensorflow/compiler/xla/service/service.cc:176] StreamExecutor device (0): Host, Default Version\n", | |
"2021-05-03 22:45:02.304706: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcuda.so.1\n", | |
"2021-05-03 22:45:02.464597: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-03 22:45:02.465331: I tensorflow/compiler/xla/service/service.cc:168] XLA service 0x555b93d53480 initialized for platform CUDA (this does not guarantee that XLA will be used). Devices:\n", | |
"2021-05-03 22:45:02.465366: I tensorflow/compiler/xla/service/service.cc:176] StreamExecutor device (0): Tesla P100-PCIE-16GB, Compute Capability 6.0\n", | |
"2021-05-03 22:45:02.466466: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-03 22:45:02.467000: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1639] Found device 0 with properties: \n", | |
"name: Tesla P100-PCIE-16GB major: 6 minor: 0 memoryClockRate(GHz): 1.3285\n", | |
"pciBusID: 0000:00:04.0\n", | |
"2021-05-03 22:45:02.481921: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.1\n", | |
"2021-05-03 22:45:02.649101: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcublas.so.10\n", | |
"2021-05-03 22:45:02.748976: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcufft.so.10\n", | |
"2021-05-03 22:45:02.786580: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcurand.so.10\n", | |
"2021-05-03 22:45:02.973976: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusolver.so.10\n", | |
"2021-05-03 22:45:02.992988: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusparse.so.10\n", | |
"2021-05-03 22:45:03.339491: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudnn.so.7\n", | |
"2021-05-03 22:45:03.339677: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-03 22:45:03.340388: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-03 22:45:03.340903: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1767] Adding visible gpu devices: 0\n", | |
"2021-05-03 22:45:03.344743: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.1\n", | |
"2021-05-03 22:45:03.346960: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1180] Device interconnect StreamExecutor with strength 1 edge matrix:\n", | |
"2021-05-03 22:45:03.346999: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1186] 0 \n", | |
"2021-05-03 22:45:03.347007: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1199] 0: N \n", | |
"2021-05-03 22:45:03.347882: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-03 22:45:03.348510: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-03 22:45:03.349029: W tensorflow/core/common_runtime/gpu/gpu_bfc_allocator.cc:39] Overriding allow_growth setting because the TF_FORCE_GPU_ALLOW_GROWTH environment variable is set. Original config value was 0.\n", | |
"2021-05-03 22:45:03.349062: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1325] Created TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 15224 MB memory) -> physical GPU (device: 0, name: Tesla P100-PCIE-16GB, pci bus id: 0000:00:04.0, compute capability: 6.0)\n", | |
"INFO:tensorflow:Running local_init_op.\n", | |
"I0503 22:45:10.319791 140215059400576 session_manager.py:500] Running local_init_op.\n", | |
"INFO:tensorflow:Done running local_init_op.\n", | |
"I0503 22:45:10.910343 140215059400576 session_manager.py:502] Done running local_init_op.\n", | |
"INFO:tensorflow:Saving checkpoints for 0 into /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt.\n", | |
"I0503 22:45:25.765369 140215059400576 basic_session_run_hooks.py:606] Saving checkpoints for 0 into /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt.\n", | |
"2021-05-03 22:45:50.528150: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcublas.so.10\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"2021-05-03 22:45:57.377577: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudnn.so.7\n", | |
"INFO:tensorflow:loss = 535.09656, step = 0\n", | |
"I0503 22:46:02.159381 140215059400576 basic_session_run_hooks.py:262] loss = 535.09656, step = 0\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:global_step/sec: 0.654843\n", | |
"I0503 22:48:34.867180 140215059400576 basic_session_run_hooks.py:692] global_step/sec: 0.654843\n", | |
"INFO:tensorflow:loss = 1.5590442, step = 100 (152.709 sec)\n", | |
"I0503 22:48:34.868344 140215059400576 basic_session_run_hooks.py:260] loss = 1.5590442, step = 100 (152.709 sec)\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:global_step/sec: 0.740994\n", | |
"I0503 22:50:49.821016 140215059400576 basic_session_run_hooks.py:692] global_step/sec: 0.740994\n", | |
"INFO:tensorflow:loss = 1.3520994, step = 200 (134.954 sec)\n", | |
"I0503 22:50:49.822021 140215059400576 basic_session_run_hooks.py:260] loss = 1.3520994, step = 200 (134.954 sec)\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:global_step/sec: 0.734331\n", | |
"I0503 22:53:05.999416 140215059400576 basic_session_run_hooks.py:692] global_step/sec: 0.734331\n", | |
"INFO:tensorflow:loss = 1.2393731, step = 300 (136.178 sec)\n", | |
"I0503 22:53:06.000502 140215059400576 basic_session_run_hooks.py:260] loss = 1.2393731, step = 300 (136.178 sec)\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:global_step/sec: 0.737303\n", | |
"I0503 22:55:21.628954 140215059400576 basic_session_run_hooks.py:692] global_step/sec: 0.737303\n", | |
"INFO:tensorflow:loss = 1.1287594, step = 400 (135.629 sec)\n", | |
"I0503 22:55:21.629845 140215059400576 basic_session_run_hooks.py:260] loss = 1.1287594, step = 400 (135.629 sec)\n", | |
"INFO:tensorflow:Saving checkpoints for 407 into /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt.\n", | |
"I0503 22:55:29.701190 140215059400576 basic_session_run_hooks.py:606] Saving checkpoints for 407 into /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt.\n", | |
"INFO:tensorflow:Reading unweighted datasets: ['/content/data/tf_record/pet_faces_train.record-?????-of-00010']\n", | |
"I0503 22:55:31.790008 140215059400576 dataset_builder.py:163] Reading unweighted datasets: ['/content/data/tf_record/pet_faces_train.record-?????-of-00010']\n", | |
"INFO:tensorflow:Reading record datasets for input file: ['/content/data/tf_record/pet_faces_train.record-?????-of-00010']\n", | |
"I0503 22:55:31.790931 140215059400576 dataset_builder.py:80] Reading record datasets for input file: ['/content/data/tf_record/pet_faces_train.record-?????-of-00010']\n", | |
"INFO:tensorflow:Number of filenames to read: 10\n", | |
"I0503 22:55:31.791066 140215059400576 dataset_builder.py:81] Number of filenames to read: 10\n", | |
"WARNING:tensorflow:Entity <bound method TfExampleDecoder.decode of <object_detection.data_decoders.tf_example_decoder.TfExampleDecoder object at 0x7f85ac22f090>> could not be transformed and will be executed as-is. Please report this to the AutoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: module 'gast' has no attribute 'Num'\n", | |
"W0503 22:55:31.832900 140215059400576 ag_logging.py:146] Entity <bound method TfExampleDecoder.decode of <object_detection.data_decoders.tf_example_decoder.TfExampleDecoder object at 0x7f85ac22f090>> could not be transformed and will be executed as-is. Please report this to the AutoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: module 'gast' has no attribute 'Num'\n", | |
"WARNING:tensorflow:Entity <function eval_input.<locals>.transform_and_pad_input_data_fn at 0x7f85ba63f710> could not be transformed and will be executed as-is. Please report this to the AutoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: Bad argument number for Name: 3, expecting 4\n", | |
"W0503 22:55:31.993654 140215059400576 ag_logging.py:146] Entity <function eval_input.<locals>.transform_and_pad_input_data_fn at 0x7f85ba63f710> could not be transformed and will be executed as-is. Please report this to the AutoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: Bad argument number for Name: 3, expecting 4\n", | |
"INFO:tensorflow:Calling model_fn.\n", | |
"I0503 22:55:32.463019 140215059400576 estimator.py:1148] Calling model_fn.\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0503 22:55:34.454580 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0503 22:55:34.524233 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0503 22:55:34.592488 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0503 22:55:34.662521 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0503 22:55:34.730876 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0503 22:55:34.799815 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"WARNING:tensorflow:From /usr/local/lib/python3.7/dist-packages/object_detection/eval_util.py:929: to_int64 (from tensorflow.python.ops.math_ops) is deprecated and will be removed in a future version.\n", | |
"Instructions for updating:\n", | |
"Use `tf.cast` instead.\n", | |
"W0503 22:55:35.840647 140215059400576 deprecation.py:323] From /usr/local/lib/python3.7/dist-packages/object_detection/eval_util.py:929: to_int64 (from tensorflow.python.ops.math_ops) is deprecated and will be removed in a future version.\n", | |
"Instructions for updating:\n", | |
"Use `tf.cast` instead.\n", | |
"WARNING:tensorflow:From /usr/local/lib/python3.7/dist-packages/object_detection/utils/visualization_utils.py:618: py_func (from tensorflow.python.ops.script_ops) is deprecated and will be removed in a future version.\n", | |
"Instructions for updating:\n", | |
"tf.py_func is deprecated in TF V2. Instead, there are two\n", | |
" options available in V2.\n", | |
" - tf.py_function takes a python function which manipulates tf eager\n", | |
" tensors instead of numpy arrays. It's easy to convert a tf eager tensor to\n", | |
" an ndarray (just call tensor.numpy()) but having access to eager tensors\n", | |
" means `tf.py_function`s can use accelerators such as GPUs as well as\n", | |
" being differentiable using a gradient tape.\n", | |
" - tf.numpy_function maintains the semantics of the deprecated tf.py_func\n", | |
" (it is not differentiable, and manipulates numpy arrays). It drops the\n", | |
" stateful argument making all functions stateful.\n", | |
" \n", | |
"W0503 22:55:36.018670 140215059400576 deprecation.py:323] From /usr/local/lib/python3.7/dist-packages/object_detection/utils/visualization_utils.py:618: py_func (from tensorflow.python.ops.script_ops) is deprecated and will be removed in a future version.\n", | |
"Instructions for updating:\n", | |
"tf.py_func is deprecated in TF V2. Instead, there are two\n", | |
" options available in V2.\n", | |
" - tf.py_function takes a python function which manipulates tf eager\n", | |
" tensors instead of numpy arrays. It's easy to convert a tf eager tensor to\n", | |
" an ndarray (just call tensor.numpy()) but having access to eager tensors\n", | |
" means `tf.py_function`s can use accelerators such as GPUs as well as\n", | |
" being differentiable using a gradient tape.\n", | |
" - tf.numpy_function maintains the semantics of the deprecated tf.py_func\n", | |
" (it is not differentiable, and manipulates numpy arrays). It drops the\n", | |
" stateful argument making all functions stateful.\n", | |
" \n", | |
"INFO:tensorflow:Done calling model_fn.\n", | |
"I0503 22:55:36.534003 140215059400576 estimator.py:1150] Done calling model_fn.\n", | |
"INFO:tensorflow:Starting evaluation at 2021-05-03T22:55:36Z\n", | |
"I0503 22:55:36.548862 140215059400576 evaluation.py:255] Starting evaluation at 2021-05-03T22:55:36Z\n", | |
"INFO:tensorflow:Graph was finalized.\n", | |
"I0503 22:55:37.000953 140215059400576 monitored_session.py:240] Graph was finalized.\n", | |
"2021-05-03 22:55:37.002135: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-03 22:55:37.002428: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1639] Found device 0 with properties: \n", | |
"name: Tesla P100-PCIE-16GB major: 6 minor: 0 memoryClockRate(GHz): 1.3285\n", | |
"pciBusID: 0000:00:04.0\n", | |
"2021-05-03 22:55:37.002548: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.1\n", | |
"2021-05-03 22:55:37.002585: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcublas.so.10\n", | |
"2021-05-03 22:55:37.002615: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcufft.so.10\n", | |
"2021-05-03 22:55:37.002646: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcurand.so.10\n", | |
"2021-05-03 22:55:37.002668: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusolver.so.10\n", | |
"2021-05-03 22:55:37.002691: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusparse.so.10\n", | |
"2021-05-03 22:55:37.002714: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudnn.so.7\n", | |
"2021-05-03 22:55:37.002806: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-03 22:55:37.003114: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-03 22:55:37.003346: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1767] Adding visible gpu devices: 0\n", | |
"2021-05-03 22:55:37.003395: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1180] Device interconnect StreamExecutor with strength 1 edge matrix:\n", | |
"2021-05-03 22:55:37.003411: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1186] 0 \n", | |
"2021-05-03 22:55:37.003421: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1199] 0: N \n", | |
"2021-05-03 22:55:37.003530: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-03 22:55:37.003797: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-03 22:55:37.004029: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1325] Created TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 15224 MB memory) -> physical GPU (device: 0, name: Tesla P100-PCIE-16GB, pci bus id: 0000:00:04.0, compute capability: 6.0)\n", | |
"INFO:tensorflow:Restoring parameters from /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt-407\n", | |
"I0503 22:55:37.004954 140215059400576 saver.py:1284] Restoring parameters from /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt-407\n", | |
"INFO:tensorflow:Running local_init_op.\n", | |
"I0503 22:55:37.990526 140215059400576 session_manager.py:500] Running local_init_op.\n", | |
"INFO:tensorflow:Done running local_init_op.\n", | |
"I0503 22:55:38.125984 140215059400576 session_manager.py:502] Done running local_init_op.\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:Performing evaluation on 2568 images.\n", | |
"I0503 22:56:40.951148 140210432521984 coco_evaluation.py:293] Performing evaluation on 2568 images.\n", | |
"creating index...\n", | |
"index created!\n", | |
"INFO:tensorflow:Loading and preparing annotation results...\n", | |
"I0503 22:56:40.959702 140210432521984 coco_tools.py:116] Loading and preparing annotation results...\n", | |
"INFO:tensorflow:DONE (t=0.19s)\n", | |
"I0503 22:56:41.151035 140210432521984 coco_tools.py:138] DONE (t=0.19s)\n", | |
"creating index...\n", | |
"index created!\n", | |
"Running per image evaluation...\n", | |
"Evaluate annotation type *bbox*\n", | |
"DONE (t=19.53s).\n", | |
"Accumulating evaluation results...\n", | |
"DONE (t=7.02s).\n", | |
" Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.072\n", | |
" Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=100 ] = 0.096\n", | |
" Average Precision (AP) @[ IoU=0.75 | area= all | maxDets=100 ] = 0.086\n", | |
" Average Precision (AP) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.000\n", | |
" Average Precision (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.049\n", | |
" Average Precision (AP) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.080\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 1 ] = 0.760\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 10 ] = 0.773\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.773\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.000\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.645\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.799\n", | |
"INFO:tensorflow:Finished evaluation at 2021-05-03-22:57:08\n", | |
"I0503 22:57:08.682882 140215059400576 evaluation.py:275] Finished evaluation at 2021-05-03-22:57:08\n", | |
"INFO:tensorflow:Saving dict for global step 407: DetectionBoxes_Precision/mAP = 0.07237052, DetectionBoxes_Precision/mAP (large) = 0.07999087, DetectionBoxes_Precision/mAP (medium) = 0.049325153, DetectionBoxes_Precision/mAP (small) = 0.0, DetectionBoxes_Precision/[email protected] = 0.09640334, DetectionBoxes_Precision/[email protected] = 0.08647537, DetectionBoxes_Recall/AR@1 = 0.75975764, DetectionBoxes_Recall/AR@10 = 0.77336717, DetectionBoxes_Recall/AR@100 = 0.77336717, DetectionBoxes_Recall/AR@100 (large) = 0.7987, DetectionBoxes_Recall/AR@100 (medium) = 0.6453445, DetectionBoxes_Recall/AR@100 (small) = 0.0, Loss/classification_loss = 0.8944688, Loss/localization_loss = 0.067470625, Loss/regularization_loss = 0.2306503, Loss/total_loss = 1.1925858, global_step = 407, learning_rate = 0.018759735, loss = 1.1925858\n", | |
"I0503 22:57:08.683235 140215059400576 estimator.py:2049] Saving dict for global step 407: DetectionBoxes_Precision/mAP = 0.07237052, DetectionBoxes_Precision/mAP (large) = 0.07999087, DetectionBoxes_Precision/mAP (medium) = 0.049325153, DetectionBoxes_Precision/mAP (small) = 0.0, DetectionBoxes_Precision/[email protected] = 0.09640334, DetectionBoxes_Precision/[email protected] = 0.08647537, DetectionBoxes_Recall/AR@1 = 0.75975764, DetectionBoxes_Recall/AR@10 = 0.77336717, DetectionBoxes_Recall/AR@100 = 0.77336717, DetectionBoxes_Recall/AR@100 (large) = 0.7987, DetectionBoxes_Recall/AR@100 (medium) = 0.6453445, DetectionBoxes_Recall/AR@100 (small) = 0.0, Loss/classification_loss = 0.8944688, Loss/localization_loss = 0.067470625, Loss/regularization_loss = 0.2306503, Loss/total_loss = 1.1925858, global_step = 407, learning_rate = 0.018759735, loss = 1.1925858\n", | |
"INFO:tensorflow:Saving 'checkpoint_path' summary for global step 407: /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt-407\n", | |
"I0503 22:57:09.511198 140215059400576 estimator.py:2109] Saving 'checkpoint_path' summary for global step 407: /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt-407\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:global_step/sec: 0.42602\n", | |
"I0503 22:59:16.359457 140215059400576 basic_session_run_hooks.py:692] global_step/sec: 0.42602\n", | |
"INFO:tensorflow:loss = 1.1076175, step = 500 (234.731 sec)\n", | |
"I0503 22:59:16.360794 140215059400576 basic_session_run_hooks.py:260] loss = 1.1076175, step = 500 (234.731 sec)\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:global_step/sec: 0.741202\n", | |
"I0503 23:01:31.275370 140215059400576 basic_session_run_hooks.py:692] global_step/sec: 0.741202\n", | |
"INFO:tensorflow:loss = 1.0399542, step = 600 (134.916 sec)\n", | |
"I0503 23:01:31.276308 140215059400576 basic_session_run_hooks.py:260] loss = 1.0399542, step = 600 (134.916 sec)\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:global_step/sec: 0.738895\n", | |
"I0503 23:03:46.612538 140215059400576 basic_session_run_hooks.py:692] global_step/sec: 0.738895\n", | |
"INFO:tensorflow:loss = 0.92504525, step = 700 (135.337 sec)\n", | |
"I0503 23:03:46.613550 140215059400576 basic_session_run_hooks.py:260] loss = 0.92504525, step = 700 (135.337 sec)\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:Saving checkpoints for 778 into /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt.\n", | |
"I0503 23:05:30.540197 140215059400576 basic_session_run_hooks.py:606] Saving checkpoints for 778 into /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt.\n", | |
"INFO:tensorflow:Reading unweighted datasets: ['/content/data/tf_record/pet_faces_train.record-?????-of-00010']\n", | |
"I0503 23:05:32.588105 140215059400576 dataset_builder.py:163] Reading unweighted datasets: ['/content/data/tf_record/pet_faces_train.record-?????-of-00010']\n", | |
"INFO:tensorflow:Reading record datasets for input file: ['/content/data/tf_record/pet_faces_train.record-?????-of-00010']\n", | |
"I0503 23:05:32.589048 140215059400576 dataset_builder.py:80] Reading record datasets for input file: ['/content/data/tf_record/pet_faces_train.record-?????-of-00010']\n", | |
"INFO:tensorflow:Number of filenames to read: 10\n", | |
"I0503 23:05:32.589216 140215059400576 dataset_builder.py:81] Number of filenames to read: 10\n", | |
"WARNING:tensorflow:Entity <bound method TfExampleDecoder.decode of <object_detection.data_decoders.tf_example_decoder.TfExampleDecoder object at 0x7f85ac180510>> could not be transformed and will be executed as-is. Please report this to the AutoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: module 'gast' has no attribute 'Num'\n", | |
"W0503 23:05:32.626588 140215059400576 ag_logging.py:146] Entity <bound method TfExampleDecoder.decode of <object_detection.data_decoders.tf_example_decoder.TfExampleDecoder object at 0x7f85ac180510>> could not be transformed and will be executed as-is. Please report this to the AutoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: module 'gast' has no attribute 'Num'\n", | |
"WARNING:tensorflow:Entity <function eval_input.<locals>.transform_and_pad_input_data_fn at 0x7f805cd06d40> could not be transformed and will be executed as-is. Please report this to the AutoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: Bad argument number for Name: 3, expecting 4\n", | |
"W0503 23:05:32.788226 140215059400576 ag_logging.py:146] Entity <function eval_input.<locals>.transform_and_pad_input_data_fn at 0x7f805cd06d40> could not be transformed and will be executed as-is. Please report this to the AutoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: Bad argument number for Name: 3, expecting 4\n", | |
"INFO:tensorflow:Calling model_fn.\n", | |
"I0503 23:05:33.322505 140215059400576 estimator.py:1148] Calling model_fn.\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0503 23:05:35.335118 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0503 23:05:35.403026 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0503 23:05:35.469755 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0503 23:05:35.536586 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0503 23:05:35.605880 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0503 23:05:35.673256 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:Done calling model_fn.\n", | |
"I0503 23:05:37.381840 140215059400576 estimator.py:1150] Done calling model_fn.\n", | |
"INFO:tensorflow:Starting evaluation at 2021-05-03T23:05:37Z\n", | |
"I0503 23:05:37.396681 140215059400576 evaluation.py:255] Starting evaluation at 2021-05-03T23:05:37Z\n", | |
"INFO:tensorflow:Graph was finalized.\n", | |
"I0503 23:05:37.850116 140215059400576 monitored_session.py:240] Graph was finalized.\n", | |
"2021-05-03 23:05:37.850790: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-03 23:05:37.851180: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1639] Found device 0 with properties: \n", | |
"name: Tesla P100-PCIE-16GB major: 6 minor: 0 memoryClockRate(GHz): 1.3285\n", | |
"pciBusID: 0000:00:04.0\n", | |
"2021-05-03 23:05:37.851323: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.1\n", | |
"2021-05-03 23:05:37.851346: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcublas.so.10\n", | |
"2021-05-03 23:05:37.851362: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcufft.so.10\n", | |
"2021-05-03 23:05:37.851377: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcurand.so.10\n", | |
"2021-05-03 23:05:37.851392: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusolver.so.10\n", | |
"2021-05-03 23:05:37.851405: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusparse.so.10\n", | |
"2021-05-03 23:05:37.851423: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudnn.so.7\n", | |
"2021-05-03 23:05:37.851498: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-03 23:05:37.851760: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-03 23:05:37.851960: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1767] Adding visible gpu devices: 0\n", | |
"2021-05-03 23:05:37.852001: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1180] Device interconnect StreamExecutor with strength 1 edge matrix:\n", | |
"2021-05-03 23:05:37.852011: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1186] 0 \n", | |
"2021-05-03 23:05:37.852018: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1199] 0: N \n", | |
"2021-05-03 23:05:37.852111: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-03 23:05:37.852376: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-03 23:05:37.852594: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1325] Created TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 15224 MB memory) -> physical GPU (device: 0, name: Tesla P100-PCIE-16GB, pci bus id: 0000:00:04.0, compute capability: 6.0)\n", | |
"INFO:tensorflow:Restoring parameters from /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt-778\n", | |
"I0503 23:05:37.853563 140215059400576 saver.py:1284] Restoring parameters from /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt-778\n", | |
"INFO:tensorflow:Running local_init_op.\n", | |
"I0503 23:05:38.816956 140215059400576 session_manager.py:500] Running local_init_op.\n", | |
"INFO:tensorflow:Done running local_init_op.\n", | |
"I0503 23:05:38.954433 140215059400576 session_manager.py:502] Done running local_init_op.\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:Performing evaluation on 2568 images.\n", | |
"I0503 23:06:42.640245 140210432521984 coco_evaluation.py:293] Performing evaluation on 2568 images.\n", | |
"creating index...\n", | |
"index created!\n", | |
"INFO:tensorflow:Loading and preparing annotation results...\n", | |
"I0503 23:06:42.647192 140210432521984 coco_tools.py:116] Loading and preparing annotation results...\n", | |
"INFO:tensorflow:DONE (t=0.20s)\n", | |
"I0503 23:06:42.843179 140210432521984 coco_tools.py:138] DONE (t=0.20s)\n", | |
"creating index...\n", | |
"index created!\n", | |
"Running per image evaluation...\n", | |
"Evaluate annotation type *bbox*\n", | |
"DONE (t=20.69s).\n", | |
"Accumulating evaluation results...\n", | |
"DONE (t=6.94s).\n", | |
" Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.281\n", | |
" Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=100 ] = 0.354\n", | |
" Average Precision (AP) @[ IoU=0.75 | area= all | maxDets=100 ] = 0.339\n", | |
" Average Precision (AP) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.000\n", | |
" Average Precision (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.107\n", | |
" Average Precision (AP) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.315\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 1 ] = 0.804\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 10 ] = 0.809\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.809\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.000\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.700\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.829\n", | |
"INFO:tensorflow:Finished evaluation at 2021-05-03-23:07:11\n", | |
"I0503 23:07:11.459625 140215059400576 evaluation.py:275] Finished evaluation at 2021-05-03-23:07:11\n", | |
"INFO:tensorflow:Saving dict for global step 778: DetectionBoxes_Precision/mAP = 0.2810173, DetectionBoxes_Precision/mAP (large) = 0.3148433, DetectionBoxes_Precision/mAP (medium) = 0.10657664, DetectionBoxes_Precision/mAP (small) = 0.0, DetectionBoxes_Precision/[email protected] = 0.35377043, DetectionBoxes_Precision/[email protected] = 0.33944526, DetectionBoxes_Recall/AR@1 = 0.80354273, DetectionBoxes_Recall/AR@10 = 0.8086807, DetectionBoxes_Recall/AR@100 = 0.8086807, DetectionBoxes_Recall/AR@100 (large) = 0.8292294, DetectionBoxes_Recall/AR@100 (medium) = 0.69966906, DetectionBoxes_Recall/AR@100 (small) = 0.0, Loss/classification_loss = 0.63184404, Loss/localization_loss = 0.049853478, Loss/regularization_loss = 0.229921, Loss/total_loss = 0.9116245, global_step = 778, learning_rate = 0.023706462, loss = 0.9116245\n", | |
"I0503 23:07:11.459895 140215059400576 estimator.py:2049] Saving dict for global step 778: DetectionBoxes_Precision/mAP = 0.2810173, DetectionBoxes_Precision/mAP (large) = 0.3148433, DetectionBoxes_Precision/mAP (medium) = 0.10657664, DetectionBoxes_Precision/mAP (small) = 0.0, DetectionBoxes_Precision/[email protected] = 0.35377043, DetectionBoxes_Precision/[email protected] = 0.33944526, DetectionBoxes_Recall/AR@1 = 0.80354273, DetectionBoxes_Recall/AR@10 = 0.8086807, DetectionBoxes_Recall/AR@100 = 0.8086807, DetectionBoxes_Recall/AR@100 (large) = 0.8292294, DetectionBoxes_Recall/AR@100 (medium) = 0.69966906, DetectionBoxes_Recall/AR@100 (small) = 0.0, Loss/classification_loss = 0.63184404, Loss/localization_loss = 0.049853478, Loss/regularization_loss = 0.229921, Loss/total_loss = 0.9116245, global_step = 778, learning_rate = 0.023706462, loss = 0.9116245\n", | |
"INFO:tensorflow:Saving 'checkpoint_path' summary for global step 778: /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt-778\n", | |
"I0503 23:07:11.463823 140215059400576 estimator.py:2109] Saving 'checkpoint_path' summary for global step 778: /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt-778\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:global_step/sec: 0.423704\n", | |
"I0503 23:07:42.626347 140215059400576 basic_session_run_hooks.py:692] global_step/sec: 0.423704\n", | |
"INFO:tensorflow:loss = 0.8872799, step = 800 (236.014 sec)\n", | |
"I0503 23:07:42.627664 140215059400576 basic_session_run_hooks.py:260] loss = 0.8872799, step = 800 (236.014 sec)\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:global_step/sec: 0.741439\n", | |
"I0503 23:09:57.499171 140215059400576 basic_session_run_hooks.py:692] global_step/sec: 0.741439\n", | |
"INFO:tensorflow:loss = 0.7907349, step = 900 (134.873 sec)\n", | |
"I0503 23:09:57.500277 140215059400576 basic_session_run_hooks.py:260] loss = 0.7907349, step = 900 (134.873 sec)\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:global_step/sec: 0.74041\n", | |
"I0503 23:12:12.559424 140215059400576 basic_session_run_hooks.py:692] global_step/sec: 0.74041\n", | |
"INFO:tensorflow:loss = 0.7546335, step = 1000 (135.060 sec)\n", | |
"I0503 23:12:12.560322 140215059400576 basic_session_run_hooks.py:260] loss = 0.7546335, step = 1000 (135.060 sec)\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:global_step/sec: 0.740813\n", | |
"I0503 23:14:27.546211 140215059400576 basic_session_run_hooks.py:692] global_step/sec: 0.740813\n", | |
"INFO:tensorflow:loss = 0.67965263, step = 1100 (134.987 sec)\n", | |
"I0503 23:14:27.547178 140215059400576 basic_session_run_hooks.py:260] loss = 0.67965263, step = 1100 (134.987 sec)\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:Saving checkpoints for 1148 into /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt.\n", | |
"I0503 23:15:30.629634 140215059400576 basic_session_run_hooks.py:606] Saving checkpoints for 1148 into /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt.\n", | |
"INFO:tensorflow:Reading unweighted datasets: ['/content/data/tf_record/pet_faces_train.record-?????-of-00010']\n", | |
"I0503 23:15:32.691810 140215059400576 dataset_builder.py:163] Reading unweighted datasets: ['/content/data/tf_record/pet_faces_train.record-?????-of-00010']\n", | |
"INFO:tensorflow:Reading record datasets for input file: ['/content/data/tf_record/pet_faces_train.record-?????-of-00010']\n", | |
"I0503 23:15:32.692809 140215059400576 dataset_builder.py:80] Reading record datasets for input file: ['/content/data/tf_record/pet_faces_train.record-?????-of-00010']\n", | |
"INFO:tensorflow:Number of filenames to read: 10\n", | |
"I0503 23:15:32.692945 140215059400576 dataset_builder.py:81] Number of filenames to read: 10\n", | |
"WARNING:tensorflow:Entity <bound method TfExampleDecoder.decode of <object_detection.data_decoders.tf_example_decoder.TfExampleDecoder object at 0x7f806463d610>> could not be transformed and will be executed as-is. Please report this to the AutoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: module 'gast' has no attribute 'Num'\n", | |
"W0503 23:15:32.730887 140215059400576 ag_logging.py:146] Entity <bound method TfExampleDecoder.decode of <object_detection.data_decoders.tf_example_decoder.TfExampleDecoder object at 0x7f806463d610>> could not be transformed and will be executed as-is. Please report this to the AutoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: module 'gast' has no attribute 'Num'\n", | |
"WARNING:tensorflow:Entity <function eval_input.<locals>.transform_and_pad_input_data_fn at 0x7f8064a3a320> could not be transformed and will be executed as-is. Please report this to the AutoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: Bad argument number for Name: 3, expecting 4\n", | |
"W0503 23:15:32.891924 140215059400576 ag_logging.py:146] Entity <function eval_input.<locals>.transform_and_pad_input_data_fn at 0x7f8064a3a320> could not be transformed and will be executed as-is. Please report this to the AutoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: Bad argument number for Name: 3, expecting 4\n", | |
"INFO:tensorflow:Calling model_fn.\n", | |
"I0503 23:15:33.377007 140215059400576 estimator.py:1148] Calling model_fn.\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0503 23:15:35.388021 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0503 23:15:35.458101 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0503 23:15:35.525646 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0503 23:15:35.593148 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0503 23:15:35.662152 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0503 23:15:35.730339 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:Done calling model_fn.\n", | |
"I0503 23:15:37.442188 140215059400576 estimator.py:1150] Done calling model_fn.\n", | |
"INFO:tensorflow:Starting evaluation at 2021-05-03T23:15:37Z\n", | |
"I0503 23:15:37.457147 140215059400576 evaluation.py:255] Starting evaluation at 2021-05-03T23:15:37Z\n", | |
"INFO:tensorflow:Graph was finalized.\n", | |
"I0503 23:15:37.906858 140215059400576 monitored_session.py:240] Graph was finalized.\n", | |
"2021-05-03 23:15:37.907585: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-03 23:15:37.907897: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1639] Found device 0 with properties: \n", | |
"name: Tesla P100-PCIE-16GB major: 6 minor: 0 memoryClockRate(GHz): 1.3285\n", | |
"pciBusID: 0000:00:04.0\n", | |
"2021-05-03 23:15:37.908022: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.1\n", | |
"2021-05-03 23:15:37.908077: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcublas.so.10\n", | |
"2021-05-03 23:15:37.908126: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcufft.so.10\n", | |
"2021-05-03 23:15:37.908152: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcurand.so.10\n", | |
"2021-05-03 23:15:37.908168: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusolver.so.10\n", | |
"2021-05-03 23:15:37.908182: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusparse.so.10\n", | |
"2021-05-03 23:15:37.908204: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudnn.so.7\n", | |
"2021-05-03 23:15:37.908287: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-03 23:15:37.908553: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-03 23:15:37.908759: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1767] Adding visible gpu devices: 0\n", | |
"2021-05-03 23:15:37.908828: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1180] Device interconnect StreamExecutor with strength 1 edge matrix:\n", | |
"2021-05-03 23:15:37.908840: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1186] 0 \n", | |
"2021-05-03 23:15:37.908847: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1199] 0: N \n", | |
"2021-05-03 23:15:37.908932: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-03 23:15:37.909208: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-03 23:15:37.909428: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1325] Created TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 15224 MB memory) -> physical GPU (device: 0, name: Tesla P100-PCIE-16GB, pci bus id: 0000:00:04.0, compute capability: 6.0)\n", | |
"INFO:tensorflow:Restoring parameters from /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt-1148\n", | |
"I0503 23:15:37.910608 140215059400576 saver.py:1284] Restoring parameters from /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt-1148\n", | |
"INFO:tensorflow:Running local_init_op.\n", | |
"I0503 23:15:38.892279 140215059400576 session_manager.py:500] Running local_init_op.\n", | |
"INFO:tensorflow:Done running local_init_op.\n", | |
"I0503 23:15:39.031018 140215059400576 session_manager.py:502] Done running local_init_op.\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:Performing evaluation on 2568 images.\n", | |
"I0503 23:16:44.290100 140210424129280 coco_evaluation.py:293] Performing evaluation on 2568 images.\n", | |
"creating index...\n", | |
"index created!\n", | |
"INFO:tensorflow:Loading and preparing annotation results...\n", | |
"I0503 23:16:44.298785 140210424129280 coco_tools.py:116] Loading and preparing annotation results...\n", | |
"INFO:tensorflow:DONE (t=0.19s)\n", | |
"I0503 23:16:44.486017 140210424129280 coco_tools.py:138] DONE (t=0.19s)\n", | |
"creating index...\n", | |
"index created!\n", | |
"Running per image evaluation...\n", | |
"Evaluate annotation type *bbox*\n", | |
"DONE (t=19.51s).\n", | |
"Accumulating evaluation results...\n", | |
"DONE (t=7.22s).\n", | |
" Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.490\n", | |
" Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=100 ] = 0.601\n", | |
" Average Precision (AP) @[ IoU=0.75 | area= all | maxDets=100 ] = 0.587\n", | |
" Average Precision (AP) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.000\n", | |
" Average Precision (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.145\n", | |
" Average Precision (AP) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.547\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 1 ] = 0.815\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 10 ] = 0.819\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.819\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.000\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.703\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.842\n", | |
"INFO:tensorflow:Finished evaluation at 2021-05-03-23:17:12\n", | |
"I0503 23:17:12.198430 140215059400576 evaluation.py:275] Finished evaluation at 2021-05-03-23:17:12\n", | |
"INFO:tensorflow:Saving dict for global step 1148: DetectionBoxes_Precision/mAP = 0.48982677, DetectionBoxes_Precision/mAP (large) = 0.5474836, DetectionBoxes_Precision/mAP (medium) = 0.14508818, DetectionBoxes_Precision/mAP (small) = 0.0, DetectionBoxes_Precision/[email protected] = 0.6014761, DetectionBoxes_Precision/[email protected] = 0.58707434, DetectionBoxes_Recall/AR@1 = 0.81457376, DetectionBoxes_Recall/AR@10 = 0.819219, DetectionBoxes_Recall/AR@100 = 0.819219, DetectionBoxes_Recall/AR@100 (large) = 0.8421357, DetectionBoxes_Recall/AR@100 (medium) = 0.7030473, DetectionBoxes_Recall/AR@100 (small) = 0.0, Loss/classification_loss = 0.4837387, Loss/localization_loss = 0.045580048, Loss/regularization_loss = 0.22921352, Loss/total_loss = 0.7585366, global_step = 1148, learning_rate = 0.028639857, loss = 0.7585366\n", | |
"I0503 23:17:12.198681 140215059400576 estimator.py:2049] Saving dict for global step 1148: DetectionBoxes_Precision/mAP = 0.48982677, DetectionBoxes_Precision/mAP (large) = 0.5474836, DetectionBoxes_Precision/mAP (medium) = 0.14508818, DetectionBoxes_Precision/mAP (small) = 0.0, DetectionBoxes_Precision/[email protected] = 0.6014761, DetectionBoxes_Precision/[email protected] = 0.58707434, DetectionBoxes_Recall/AR@1 = 0.81457376, DetectionBoxes_Recall/AR@10 = 0.819219, DetectionBoxes_Recall/AR@100 = 0.819219, DetectionBoxes_Recall/AR@100 (large) = 0.8421357, DetectionBoxes_Recall/AR@100 (medium) = 0.7030473, DetectionBoxes_Recall/AR@100 (small) = 0.0, Loss/classification_loss = 0.4837387, Loss/localization_loss = 0.045580048, Loss/regularization_loss = 0.22921352, Loss/total_loss = 0.7585366, global_step = 1148, learning_rate = 0.028639857, loss = 0.7585366\n", | |
"INFO:tensorflow:Saving 'checkpoint_path' summary for global step 1148: /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt-1148\n", | |
"I0503 23:17:12.202373 140215059400576 estimator.py:2109] Saving 'checkpoint_path' summary for global step 1148: /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt-1148\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:global_step/sec: 0.422406\n", | |
"I0503 23:18:24.285328 140215059400576 basic_session_run_hooks.py:692] global_step/sec: 0.422406\n", | |
"INFO:tensorflow:loss = 0.6914617, step = 1200 (236.739 sec)\n", | |
"I0503 23:18:24.286469 140215059400576 basic_session_run_hooks.py:260] loss = 0.6914617, step = 1200 (236.739 sec)\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:global_step/sec: 0.739784\n", | |
"I0503 23:20:39.459912 140215059400576 basic_session_run_hooks.py:692] global_step/sec: 0.739784\n", | |
"INFO:tensorflow:loss = 0.6413725, step = 1300 (135.175 sec)\n", | |
"I0503 23:20:39.461007 140215059400576 basic_session_run_hooks.py:260] loss = 0.6413725, step = 1300 (135.175 sec)\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:global_step/sec: 0.741462\n", | |
"I0503 23:22:54.328642 140215059400576 basic_session_run_hooks.py:692] global_step/sec: 0.741462\n", | |
"INFO:tensorflow:loss = 0.64289474, step = 1400 (134.869 sec)\n", | |
"I0503 23:22:54.329862 140215059400576 basic_session_run_hooks.py:260] loss = 0.64289474, step = 1400 (134.869 sec)\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:global_step/sec: 0.741569\n", | |
"I0503 23:25:09.177831 140215059400576 basic_session_run_hooks.py:692] global_step/sec: 0.741569\n", | |
"INFO:tensorflow:loss = 0.6105086, step = 1500 (134.849 sec)\n", | |
"I0503 23:25:09.178897 140215059400576 basic_session_run_hooks.py:260] loss = 0.6105086, step = 1500 (134.849 sec)\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:Saving checkpoints for 1517 into /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt.\n", | |
"I0503 23:25:30.690894 140215059400576 basic_session_run_hooks.py:606] Saving checkpoints for 1517 into /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt.\n", | |
"INFO:tensorflow:Reading unweighted datasets: ['/content/data/tf_record/pet_faces_train.record-?????-of-00010']\n", | |
"I0503 23:25:32.766442 140215059400576 dataset_builder.py:163] Reading unweighted datasets: ['/content/data/tf_record/pet_faces_train.record-?????-of-00010']\n", | |
"INFO:tensorflow:Reading record datasets for input file: ['/content/data/tf_record/pet_faces_train.record-?????-of-00010']\n", | |
"I0503 23:25:32.767466 140215059400576 dataset_builder.py:80] Reading record datasets for input file: ['/content/data/tf_record/pet_faces_train.record-?????-of-00010']\n", | |
"INFO:tensorflow:Number of filenames to read: 10\n", | |
"I0503 23:25:32.767613 140215059400576 dataset_builder.py:81] Number of filenames to read: 10\n", | |
"WARNING:tensorflow:Entity <bound method TfExampleDecoder.decode of <object_detection.data_decoders.tf_example_decoder.TfExampleDecoder object at 0x7f852809bad0>> could not be transformed and will be executed as-is. Please report this to the AutoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: module 'gast' has no attribute 'Num'\n", | |
"W0503 23:25:32.805515 140215059400576 ag_logging.py:146] Entity <bound method TfExampleDecoder.decode of <object_detection.data_decoders.tf_example_decoder.TfExampleDecoder object at 0x7f852809bad0>> could not be transformed and will be executed as-is. Please report this to the AutoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: module 'gast' has no attribute 'Num'\n", | |
"WARNING:tensorflow:Entity <function eval_input.<locals>.transform_and_pad_input_data_fn at 0x7f85ac1f1d40> could not be transformed and will be executed as-is. Please report this to the AutoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: Bad argument number for Name: 3, expecting 4\n", | |
"W0503 23:25:32.967390 140215059400576 ag_logging.py:146] Entity <function eval_input.<locals>.transform_and_pad_input_data_fn at 0x7f85ac1f1d40> could not be transformed and will be executed as-is. Please report this to the AutoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: Bad argument number for Name: 3, expecting 4\n", | |
"INFO:tensorflow:Calling model_fn.\n", | |
"I0503 23:25:33.443732 140215059400576 estimator.py:1148] Calling model_fn.\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0503 23:25:35.480487 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0503 23:25:35.550337 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0503 23:25:35.618407 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0503 23:25:35.687046 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0503 23:25:35.756820 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0503 23:25:35.826197 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:Done calling model_fn.\n", | |
"I0503 23:25:37.560714 140215059400576 estimator.py:1150] Done calling model_fn.\n", | |
"INFO:tensorflow:Starting evaluation at 2021-05-03T23:25:37Z\n", | |
"I0503 23:25:37.575657 140215059400576 evaluation.py:255] Starting evaluation at 2021-05-03T23:25:37Z\n", | |
"INFO:tensorflow:Graph was finalized.\n", | |
"I0503 23:25:38.028902 140215059400576 monitored_session.py:240] Graph was finalized.\n", | |
"2021-05-03 23:25:38.029615: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-03 23:25:38.029984: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1639] Found device 0 with properties: \n", | |
"name: Tesla P100-PCIE-16GB major: 6 minor: 0 memoryClockRate(GHz): 1.3285\n", | |
"pciBusID: 0000:00:04.0\n", | |
"2021-05-03 23:25:38.030103: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.1\n", | |
"2021-05-03 23:25:38.030129: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcublas.so.10\n", | |
"2021-05-03 23:25:38.030146: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcufft.so.10\n", | |
"2021-05-03 23:25:38.030161: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcurand.so.10\n", | |
"2021-05-03 23:25:38.030174: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusolver.so.10\n", | |
"2021-05-03 23:25:38.030188: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusparse.so.10\n", | |
"2021-05-03 23:25:38.030202: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudnn.so.7\n", | |
"2021-05-03 23:25:38.030274: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-03 23:25:38.030521: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-03 23:25:38.030711: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1767] Adding visible gpu devices: 0\n", | |
"2021-05-03 23:25:38.030750: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1180] Device interconnect StreamExecutor with strength 1 edge matrix:\n", | |
"2021-05-03 23:25:38.030769: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1186] 0 \n", | |
"2021-05-03 23:25:38.030775: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1199] 0: N \n", | |
"2021-05-03 23:25:38.030852: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-03 23:25:38.031104: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-03 23:25:38.031307: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1325] Created TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 15224 MB memory) -> physical GPU (device: 0, name: Tesla P100-PCIE-16GB, pci bus id: 0000:00:04.0, compute capability: 6.0)\n", | |
"INFO:tensorflow:Restoring parameters from /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt-1517\n", | |
"I0503 23:25:38.032435 140215059400576 saver.py:1284] Restoring parameters from /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt-1517\n", | |
"INFO:tensorflow:Running local_init_op.\n", | |
"I0503 23:25:39.018480 140215059400576 session_manager.py:500] Running local_init_op.\n", | |
"INFO:tensorflow:Done running local_init_op.\n", | |
"I0503 23:25:39.154753 140215059400576 session_manager.py:502] Done running local_init_op.\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:Performing evaluation on 2568 images.\n", | |
"I0503 23:26:44.479240 140210432521984 coco_evaluation.py:293] Performing evaluation on 2568 images.\n", | |
"creating index...\n", | |
"index created!\n", | |
"INFO:tensorflow:Loading and preparing annotation results...\n", | |
"I0503 23:26:44.485937 140210432521984 coco_tools.py:116] Loading and preparing annotation results...\n", | |
"INFO:tensorflow:DONE (t=0.18s)\n", | |
"I0503 23:26:44.670150 140210432521984 coco_tools.py:138] DONE (t=0.18s)\n", | |
"creating index...\n", | |
"index created!\n", | |
"Running per image evaluation...\n", | |
"Evaluate annotation type *bbox*\n", | |
"DONE (t=19.66s).\n", | |
"Accumulating evaluation results...\n", | |
"DONE (t=6.90s).\n", | |
" Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.632\n", | |
" Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=100 ] = 0.796\n", | |
" Average Precision (AP) @[ IoU=0.75 | area= all | maxDets=100 ] = 0.756\n", | |
" Average Precision (AP) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.000\n", | |
" Average Precision (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.195\n", | |
" Average Precision (AP) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.703\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 1 ] = 0.799\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 10 ] = 0.810\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.810\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.000\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.684\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.835\n", | |
"INFO:tensorflow:Finished evaluation at 2021-05-03-23:27:12\n", | |
"I0503 23:27:12.192707 140215059400576 evaluation.py:275] Finished evaluation at 2021-05-03-23:27:12\n", | |
"INFO:tensorflow:Saving dict for global step 1517: DetectionBoxes_Precision/mAP = 0.6323059, DetectionBoxes_Precision/mAP (large) = 0.70349455, DetectionBoxes_Precision/mAP (medium) = 0.19478361, DetectionBoxes_Precision/mAP (small) = 0.0, DetectionBoxes_Precision/[email protected] = 0.79594624, DetectionBoxes_Precision/[email protected] = 0.7563676, DetectionBoxes_Recall/AR@1 = 0.7993223, DetectionBoxes_Recall/AR@10 = 0.80954814, DetectionBoxes_Recall/AR@100 = 0.80954814, DetectionBoxes_Recall/AR@100 (large) = 0.83493155, DetectionBoxes_Recall/AR@100 (medium) = 0.6838918, DetectionBoxes_Recall/AR@100 (small) = 0.0, Loss/classification_loss = 0.39249372, Loss/localization_loss = 0.051634457, Loss/regularization_loss = 0.22847962, Loss/total_loss = 0.6726066, global_step = 1517, learning_rate = 0.03355992, loss = 0.6726066\n", | |
"I0503 23:27:12.192950 140215059400576 estimator.py:2049] Saving dict for global step 1517: DetectionBoxes_Precision/mAP = 0.6323059, DetectionBoxes_Precision/mAP (large) = 0.70349455, DetectionBoxes_Precision/mAP (medium) = 0.19478361, DetectionBoxes_Precision/mAP (small) = 0.0, DetectionBoxes_Precision/[email protected] = 0.79594624, DetectionBoxes_Precision/[email protected] = 0.7563676, DetectionBoxes_Recall/AR@1 = 0.7993223, DetectionBoxes_Recall/AR@10 = 0.80954814, DetectionBoxes_Recall/AR@100 = 0.80954814, DetectionBoxes_Recall/AR@100 (large) = 0.83493155, DetectionBoxes_Recall/AR@100 (medium) = 0.6838918, DetectionBoxes_Recall/AR@100 (small) = 0.0, Loss/classification_loss = 0.39249372, Loss/localization_loss = 0.051634457, Loss/regularization_loss = 0.22847962, Loss/total_loss = 0.6726066, global_step = 1517, learning_rate = 0.03355992, loss = 0.6726066\n", | |
"INFO:tensorflow:Saving 'checkpoint_path' summary for global step 1517: /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt-1517\n", | |
"I0503 23:27:12.196601 140215059400576 estimator.py:2109] Saving 'checkpoint_path' summary for global step 1517: /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt-1517\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:global_step/sec: 0.422346\n", | |
"I0503 23:29:05.950512 140215059400576 basic_session_run_hooks.py:692] global_step/sec: 0.422346\n", | |
"INFO:tensorflow:loss = 0.5302361, step = 1600 (236.772 sec)\n", | |
"I0503 23:29:05.951356 140215059400576 basic_session_run_hooks.py:260] loss = 0.5302361, step = 1600 (236.772 sec)\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:global_step/sec: 0.741342\n", | |
"I0503 23:31:20.840980 140215059400576 basic_session_run_hooks.py:692] global_step/sec: 0.741342\n", | |
"INFO:tensorflow:loss = 0.5171362, step = 1700 (134.891 sec)\n", | |
"I0503 23:31:20.841886 140215059400576 basic_session_run_hooks.py:260] loss = 0.5171362, step = 1700 (134.891 sec)\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:global_step/sec: 0.740093\n", | |
"I0503 23:33:35.959067 140215059400576 basic_session_run_hooks.py:692] global_step/sec: 0.740093\n", | |
"INFO:tensorflow:loss = 0.50612265, step = 1800 (135.118 sec)\n", | |
"I0503 23:33:35.960165 140215059400576 basic_session_run_hooks.py:260] loss = 0.50612265, step = 1800 (135.118 sec)\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:Saving checkpoints for 1887 into /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt.\n", | |
"I0503 23:35:31.814470 140215059400576 basic_session_run_hooks.py:606] Saving checkpoints for 1887 into /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt.\n", | |
"WARNING:tensorflow:From /tensorflow-1.15.2/python3.7/tensorflow_core/python/training/saver.py:963: remove_checkpoint (from tensorflow.python.training.checkpoint_management) is deprecated and will be removed in a future version.\n", | |
"Instructions for updating:\n", | |
"Use standard file APIs to delete files with this prefix.\n", | |
"W0503 23:35:31.851176 140215059400576 deprecation.py:323] From /tensorflow-1.15.2/python3.7/tensorflow_core/python/training/saver.py:963: remove_checkpoint (from tensorflow.python.training.checkpoint_management) is deprecated and will be removed in a future version.\n", | |
"Instructions for updating:\n", | |
"Use standard file APIs to delete files with this prefix.\n", | |
"INFO:tensorflow:Reading unweighted datasets: ['/content/data/tf_record/pet_faces_train.record-?????-of-00010']\n", | |
"I0503 23:35:33.899321 140215059400576 dataset_builder.py:163] Reading unweighted datasets: ['/content/data/tf_record/pet_faces_train.record-?????-of-00010']\n", | |
"INFO:tensorflow:Reading record datasets for input file: ['/content/data/tf_record/pet_faces_train.record-?????-of-00010']\n", | |
"I0503 23:35:33.900343 140215059400576 dataset_builder.py:80] Reading record datasets for input file: ['/content/data/tf_record/pet_faces_train.record-?????-of-00010']\n", | |
"INFO:tensorflow:Number of filenames to read: 10\n", | |
"I0503 23:35:33.900473 140215059400576 dataset_builder.py:81] Number of filenames to read: 10\n", | |
"WARNING:tensorflow:Entity <bound method TfExampleDecoder.decode of <object_detection.data_decoders.tf_example_decoder.TfExampleDecoder object at 0x7f8061e9df10>> could not be transformed and will be executed as-is. Please report this to the AutoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: module 'gast' has no attribute 'Num'\n", | |
"W0503 23:35:33.938002 140215059400576 ag_logging.py:146] Entity <bound method TfExampleDecoder.decode of <object_detection.data_decoders.tf_example_decoder.TfExampleDecoder object at 0x7f8061e9df10>> could not be transformed and will be executed as-is. Please report this to the AutoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: module 'gast' has no attribute 'Num'\n", | |
"WARNING:tensorflow:Entity <function eval_input.<locals>.transform_and_pad_input_data_fn at 0x7f85a0369830> could not be transformed and will be executed as-is. Please report this to the AutoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: Bad argument number for Name: 3, expecting 4\n", | |
"W0503 23:35:34.098869 140215059400576 ag_logging.py:146] Entity <function eval_input.<locals>.transform_and_pad_input_data_fn at 0x7f85a0369830> could not be transformed and will be executed as-is. Please report this to the AutoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: Bad argument number for Name: 3, expecting 4\n", | |
"INFO:tensorflow:Calling model_fn.\n", | |
"I0503 23:35:34.580175 140215059400576 estimator.py:1148] Calling model_fn.\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0503 23:35:36.596133 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0503 23:35:36.664330 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0503 23:35:36.732290 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0503 23:35:36.799439 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0503 23:35:36.868535 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0503 23:35:36.937592 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:Done calling model_fn.\n", | |
"I0503 23:35:38.644671 140215059400576 estimator.py:1150] Done calling model_fn.\n", | |
"INFO:tensorflow:Starting evaluation at 2021-05-03T23:35:38Z\n", | |
"I0503 23:35:38.659392 140215059400576 evaluation.py:255] Starting evaluation at 2021-05-03T23:35:38Z\n", | |
"INFO:tensorflow:Graph was finalized.\n", | |
"I0503 23:35:39.110249 140215059400576 monitored_session.py:240] Graph was finalized.\n", | |
"2021-05-03 23:35:39.110821: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-03 23:35:39.111126: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1639] Found device 0 with properties: \n", | |
"name: Tesla P100-PCIE-16GB major: 6 minor: 0 memoryClockRate(GHz): 1.3285\n", | |
"pciBusID: 0000:00:04.0\n", | |
"2021-05-03 23:35:39.111218: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.1\n", | |
"2021-05-03 23:35:39.111240: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcublas.so.10\n", | |
"2021-05-03 23:35:39.111255: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcufft.so.10\n", | |
"2021-05-03 23:35:39.111273: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcurand.so.10\n", | |
"2021-05-03 23:35:39.111313: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusolver.so.10\n", | |
"2021-05-03 23:35:39.111326: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusparse.so.10\n", | |
"2021-05-03 23:35:39.111341: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudnn.so.7\n", | |
"2021-05-03 23:35:39.111410: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-03 23:35:39.111657: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-03 23:35:39.111844: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1767] Adding visible gpu devices: 0\n", | |
"2021-05-03 23:35:39.111918: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1180] Device interconnect StreamExecutor with strength 1 edge matrix:\n", | |
"2021-05-03 23:35:39.111930: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1186] 0 \n", | |
"2021-05-03 23:35:39.111936: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1199] 0: N \n", | |
"2021-05-03 23:35:39.112023: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-03 23:35:39.112268: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-03 23:35:39.112467: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1325] Created TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 15224 MB memory) -> physical GPU (device: 0, name: Tesla P100-PCIE-16GB, pci bus id: 0000:00:04.0, compute capability: 6.0)\n", | |
"INFO:tensorflow:Restoring parameters from /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt-1887\n", | |
"I0503 23:35:39.113397 140215059400576 saver.py:1284] Restoring parameters from /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt-1887\n", | |
"INFO:tensorflow:Running local_init_op.\n", | |
"I0503 23:35:40.097286 140215059400576 session_manager.py:500] Running local_init_op.\n", | |
"INFO:tensorflow:Done running local_init_op.\n", | |
"I0503 23:35:40.235534 140215059400576 session_manager.py:502] Done running local_init_op.\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:Performing evaluation on 2568 images.\n", | |
"I0503 23:36:45.215229 140210415736576 coco_evaluation.py:293] Performing evaluation on 2568 images.\n", | |
"creating index...\n", | |
"index created!\n", | |
"INFO:tensorflow:Loading and preparing annotation results...\n", | |
"I0503 23:36:45.220566 140210415736576 coco_tools.py:116] Loading and preparing annotation results...\n", | |
"INFO:tensorflow:DONE (t=0.19s)\n", | |
"I0503 23:36:45.412241 140210415736576 coco_tools.py:138] DONE (t=0.19s)\n", | |
"creating index...\n", | |
"index created!\n", | |
"Running per image evaluation...\n", | |
"Evaluate annotation type *bbox*\n", | |
"DONE (t=19.16s).\n", | |
"Accumulating evaluation results...\n", | |
"DONE (t=7.61s).\n", | |
" Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.672\n", | |
" Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=100 ] = 0.826\n", | |
" Average Precision (AP) @[ IoU=0.75 | area= all | maxDets=100 ] = 0.799\n", | |
" Average Precision (AP) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.000\n", | |
" Average Precision (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.189\n", | |
" Average Precision (AP) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.747\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 1 ] = 0.822\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 10 ] = 0.829\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.829\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.000\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.714\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.852\n", | |
"INFO:tensorflow:Finished evaluation at 2021-05-03-23:37:13\n", | |
"I0503 23:37:13.162960 140215059400576 evaluation.py:275] Finished evaluation at 2021-05-03-23:37:13\n", | |
"INFO:tensorflow:Saving dict for global step 1887: DetectionBoxes_Precision/mAP = 0.67242706, DetectionBoxes_Precision/mAP (large) = 0.74694633, DetectionBoxes_Precision/mAP (medium) = 0.18869188, DetectionBoxes_Precision/mAP (small) = 0.0, DetectionBoxes_Precision/[email protected] = 0.82617134, DetectionBoxes_Precision/[email protected] = 0.79909503, DetectionBoxes_Recall/AR@1 = 0.82155824, DetectionBoxes_Recall/AR@10 = 0.82916605, DetectionBoxes_Recall/AR@100 = 0.8292433, DetectionBoxes_Recall/AR@100 (large) = 0.85227907, DetectionBoxes_Recall/AR@100 (medium) = 0.7142042, DetectionBoxes_Recall/AR@100 (small) = 0.0, Loss/classification_loss = 0.35495389, Loss/localization_loss = 0.044486213, Loss/regularization_loss = 0.22757879, Loss/total_loss = 0.62702036, global_step = 1887, learning_rate = 0.038493313, loss = 0.62702036\n", | |
"I0503 23:37:13.163253 140215059400576 estimator.py:2049] Saving dict for global step 1887: DetectionBoxes_Precision/mAP = 0.67242706, DetectionBoxes_Precision/mAP (large) = 0.74694633, DetectionBoxes_Precision/mAP (medium) = 0.18869188, DetectionBoxes_Precision/mAP (small) = 0.0, DetectionBoxes_Precision/[email protected] = 0.82617134, DetectionBoxes_Precision/[email protected] = 0.79909503, DetectionBoxes_Recall/AR@1 = 0.82155824, DetectionBoxes_Recall/AR@10 = 0.82916605, DetectionBoxes_Recall/AR@100 = 0.8292433, DetectionBoxes_Recall/AR@100 (large) = 0.85227907, DetectionBoxes_Recall/AR@100 (medium) = 0.7142042, DetectionBoxes_Recall/AR@100 (small) = 0.0, Loss/classification_loss = 0.35495389, Loss/localization_loss = 0.044486213, Loss/regularization_loss = 0.22757879, Loss/total_loss = 0.62702036, global_step = 1887, learning_rate = 0.038493313, loss = 0.62702036\n", | |
"INFO:tensorflow:Saving 'checkpoint_path' summary for global step 1887: /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt-1887\n", | |
"I0503 23:37:13.166974 140215059400576 estimator.py:2109] Saving 'checkpoint_path' summary for global step 1887: /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt-1887\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:global_step/sec: 0.42351\n", | |
"I0503 23:37:32.081185 140215059400576 basic_session_run_hooks.py:692] global_step/sec: 0.42351\n", | |
"INFO:tensorflow:loss = 0.5166437, step = 1900 (236.122 sec)\n", | |
"I0503 23:37:32.082311 140215059400576 basic_session_run_hooks.py:260] loss = 0.5166437, step = 1900 (236.122 sec)\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:global_step/sec: 0.739704\n", | |
"I0503 23:39:47.270425 140215059400576 basic_session_run_hooks.py:692] global_step/sec: 0.739704\n", | |
"INFO:tensorflow:loss = 0.50334144, step = 2000 (135.189 sec)\n", | |
"I0503 23:39:47.271392 140215059400576 basic_session_run_hooks.py:260] loss = 0.50334144, step = 2000 (135.189 sec)\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:global_step/sec: 0.738424\n", | |
"I0503 23:42:02.694048 140215059400576 basic_session_run_hooks.py:692] global_step/sec: 0.738424\n", | |
"INFO:tensorflow:loss = 0.50477946, step = 2100 (135.424 sec)\n", | |
"I0503 23:42:02.695218 140215059400576 basic_session_run_hooks.py:260] loss = 0.50477946, step = 2100 (135.424 sec)\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:global_step/sec: 0.740113\n", | |
"I0503 23:44:17.808477 140215059400576 basic_session_run_hooks.py:692] global_step/sec: 0.740113\n", | |
"INFO:tensorflow:loss = 0.47339472, step = 2200 (135.114 sec)\n", | |
"I0503 23:44:17.809370 140215059400576 basic_session_run_hooks.py:260] loss = 0.47339472, step = 2200 (135.114 sec)\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:Saving checkpoints for 2256 into /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt.\n", | |
"I0503 23:45:32.084156 140215059400576 basic_session_run_hooks.py:606] Saving checkpoints for 2256 into /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt.\n", | |
"INFO:tensorflow:Reading unweighted datasets: ['/content/data/tf_record/pet_faces_train.record-?????-of-00010']\n", | |
"I0503 23:45:34.181569 140215059400576 dataset_builder.py:163] Reading unweighted datasets: ['/content/data/tf_record/pet_faces_train.record-?????-of-00010']\n", | |
"INFO:tensorflow:Reading record datasets for input file: ['/content/data/tf_record/pet_faces_train.record-?????-of-00010']\n", | |
"I0503 23:45:34.182606 140215059400576 dataset_builder.py:80] Reading record datasets for input file: ['/content/data/tf_record/pet_faces_train.record-?????-of-00010']\n", | |
"INFO:tensorflow:Number of filenames to read: 10\n", | |
"I0503 23:45:34.182740 140215059400576 dataset_builder.py:81] Number of filenames to read: 10\n", | |
"WARNING:tensorflow:Entity <bound method TfExampleDecoder.decode of <object_detection.data_decoders.tf_example_decoder.TfExampleDecoder object at 0x7f8064e651d0>> could not be transformed and will be executed as-is. Please report this to the AutoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: module 'gast' has no attribute 'Num'\n", | |
"W0503 23:45:34.220583 140215059400576 ag_logging.py:146] Entity <bound method TfExampleDecoder.decode of <object_detection.data_decoders.tf_example_decoder.TfExampleDecoder object at 0x7f8064e651d0>> could not be transformed and will be executed as-is. Please report this to the AutoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: module 'gast' has no attribute 'Num'\n", | |
"WARNING:tensorflow:Entity <function eval_input.<locals>.transform_and_pad_input_data_fn at 0x7f8063b79200> could not be transformed and will be executed as-is. Please report this to the AutoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: Bad argument number for Name: 3, expecting 4\n", | |
"W0503 23:45:34.381741 140215059400576 ag_logging.py:146] Entity <function eval_input.<locals>.transform_and_pad_input_data_fn at 0x7f8063b79200> could not be transformed and will be executed as-is. Please report this to the AutoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: Bad argument number for Name: 3, expecting 4\n", | |
"INFO:tensorflow:Calling model_fn.\n", | |
"I0503 23:45:34.859106 140215059400576 estimator.py:1148] Calling model_fn.\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0503 23:45:36.859054 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0503 23:45:36.928016 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0503 23:45:36.996630 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0503 23:45:37.065469 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0503 23:45:37.133673 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0503 23:45:37.203128 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:Done calling model_fn.\n", | |
"I0503 23:45:38.929836 140215059400576 estimator.py:1150] Done calling model_fn.\n", | |
"INFO:tensorflow:Starting evaluation at 2021-05-03T23:45:38Z\n", | |
"I0503 23:45:38.944777 140215059400576 evaluation.py:255] Starting evaluation at 2021-05-03T23:45:38Z\n", | |
"INFO:tensorflow:Graph was finalized.\n", | |
"I0503 23:45:39.392005 140215059400576 monitored_session.py:240] Graph was finalized.\n", | |
"2021-05-03 23:45:39.392753: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-03 23:45:39.393127: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1639] Found device 0 with properties: \n", | |
"name: Tesla P100-PCIE-16GB major: 6 minor: 0 memoryClockRate(GHz): 1.3285\n", | |
"pciBusID: 0000:00:04.0\n", | |
"2021-05-03 23:45:39.393243: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.1\n", | |
"2021-05-03 23:45:39.393275: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcublas.so.10\n", | |
"2021-05-03 23:45:39.393300: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcufft.so.10\n", | |
"2021-05-03 23:45:39.393325: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcurand.so.10\n", | |
"2021-05-03 23:45:39.393353: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusolver.so.10\n", | |
"2021-05-03 23:45:39.393376: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusparse.so.10\n", | |
"2021-05-03 23:45:39.393400: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudnn.so.7\n", | |
"2021-05-03 23:45:39.393493: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-03 23:45:39.393766: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-03 23:45:39.393977: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1767] Adding visible gpu devices: 0\n", | |
"2021-05-03 23:45:39.394021: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1180] Device interconnect StreamExecutor with strength 1 edge matrix:\n", | |
"2021-05-03 23:45:39.394036: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1186] 0 \n", | |
"2021-05-03 23:45:39.394046: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1199] 0: N \n", | |
"2021-05-03 23:45:39.394161: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-03 23:45:39.394432: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-03 23:45:39.394647: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1325] Created TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 15224 MB memory) -> physical GPU (device: 0, name: Tesla P100-PCIE-16GB, pci bus id: 0000:00:04.0, compute capability: 6.0)\n", | |
"INFO:tensorflow:Restoring parameters from /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt-2256\n", | |
"I0503 23:45:39.395557 140215059400576 saver.py:1284] Restoring parameters from /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt-2256\n", | |
"INFO:tensorflow:Running local_init_op.\n", | |
"I0503 23:45:40.371191 140215059400576 session_manager.py:500] Running local_init_op.\n", | |
"INFO:tensorflow:Done running local_init_op.\n", | |
"I0503 23:45:40.509582 140215059400576 session_manager.py:502] Done running local_init_op.\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:Performing evaluation on 2568 images.\n", | |
"I0503 23:46:45.338707 140210432521984 coco_evaluation.py:293] Performing evaluation on 2568 images.\n", | |
"creating index...\n", | |
"index created!\n", | |
"INFO:tensorflow:Loading and preparing annotation results...\n", | |
"I0503 23:46:45.344563 140210432521984 coco_tools.py:116] Loading and preparing annotation results...\n", | |
"INFO:tensorflow:DONE (t=0.19s)\n", | |
"I0503 23:46:45.535475 140210432521984 coco_tools.py:138] DONE (t=0.19s)\n", | |
"creating index...\n", | |
"index created!\n", | |
"Running per image evaluation...\n", | |
"Evaluate annotation type *bbox*\n", | |
"DONE (t=19.42s).\n", | |
"Accumulating evaluation results...\n", | |
"DONE (t=6.71s).\n", | |
" Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.720\n", | |
" Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=100 ] = 0.864\n", | |
" Average Precision (AP) @[ IoU=0.75 | area= all | maxDets=100 ] = 0.841\n", | |
" Average Precision (AP) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.000\n", | |
" Average Precision (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.241\n", | |
" Average Precision (AP) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.788\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 1 ] = 0.840\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 10 ] = 0.845\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.845\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.000\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.713\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.870\n", | |
"INFO:tensorflow:Finished evaluation at 2021-05-03-23:47:12\n", | |
"I0503 23:47:12.657825 140215059400576 evaluation.py:275] Finished evaluation at 2021-05-03-23:47:12\n", | |
"INFO:tensorflow:Saving dict for global step 2256: DetectionBoxes_Precision/mAP = 0.7203577, DetectionBoxes_Precision/mAP (large) = 0.78831357, DetectionBoxes_Precision/mAP (medium) = 0.2406104, DetectionBoxes_Precision/mAP (small) = 0.0, DetectionBoxes_Precision/[email protected] = 0.86428666, DetectionBoxes_Precision/[email protected] = 0.84071743, DetectionBoxes_Recall/AR@1 = 0.83993936, DetectionBoxes_Recall/AR@10 = 0.8446778, DetectionBoxes_Recall/AR@100 = 0.8446778, DetectionBoxes_Recall/AR@100 (large) = 0.87, DetectionBoxes_Recall/AR@100 (medium) = 0.71340513, DetectionBoxes_Recall/AR@100 (small) = 0.0, Loss/classification_loss = 0.31298685, Loss/localization_loss = 0.036284216, Loss/regularization_loss = 0.22650018, Loss/total_loss = 0.57576823, global_step = 2256, learning_rate = 0.039999958, loss = 0.57576823\n", | |
"I0503 23:47:12.658100 140215059400576 estimator.py:2049] Saving dict for global step 2256: DetectionBoxes_Precision/mAP = 0.7203577, DetectionBoxes_Precision/mAP (large) = 0.78831357, DetectionBoxes_Precision/mAP (medium) = 0.2406104, DetectionBoxes_Precision/mAP (small) = 0.0, DetectionBoxes_Precision/[email protected] = 0.86428666, DetectionBoxes_Precision/[email protected] = 0.84071743, DetectionBoxes_Recall/AR@1 = 0.83993936, DetectionBoxes_Recall/AR@10 = 0.8446778, DetectionBoxes_Recall/AR@100 = 0.8446778, DetectionBoxes_Recall/AR@100 (large) = 0.87, DetectionBoxes_Recall/AR@100 (medium) = 0.71340513, DetectionBoxes_Recall/AR@100 (small) = 0.0, Loss/classification_loss = 0.31298685, Loss/localization_loss = 0.036284216, Loss/regularization_loss = 0.22650018, Loss/total_loss = 0.57576823, global_step = 2256, learning_rate = 0.039999958, loss = 0.57576823\n", | |
"INFO:tensorflow:Saving 'checkpoint_path' summary for global step 2256: /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt-2256\n", | |
"I0503 23:47:12.661755 140215059400576 estimator.py:2109] Saving 'checkpoint_path' summary for global step 2256: /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt-2256\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:global_step/sec: 0.425083\n", | |
"I0503 23:48:13.056861 140215059400576 basic_session_run_hooks.py:692] global_step/sec: 0.425083\n", | |
"INFO:tensorflow:loss = 0.48110726, step = 2300 (235.248 sec)\n", | |
"I0503 23:48:13.057757 140215059400576 basic_session_run_hooks.py:260] loss = 0.48110726, step = 2300 (235.248 sec)\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:global_step/sec: 0.740782\n", | |
"I0503 23:50:28.049423 140215059400576 basic_session_run_hooks.py:692] global_step/sec: 0.740782\n", | |
"INFO:tensorflow:loss = 0.4774669, step = 2400 (134.993 sec)\n", | |
"I0503 23:50:28.050292 140215059400576 basic_session_run_hooks.py:260] loss = 0.4774669, step = 2400 (134.993 sec)\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:global_step/sec: 0.74331\n", | |
"I0503 23:52:42.582801 140215059400576 basic_session_run_hooks.py:692] global_step/sec: 0.74331\n", | |
"INFO:tensorflow:loss = 0.51193935, step = 2500 (134.533 sec)\n", | |
"I0503 23:52:42.583755 140215059400576 basic_session_run_hooks.py:260] loss = 0.51193935, step = 2500 (134.533 sec)\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:global_step/sec: 0.741872\n", | |
"I0503 23:54:57.376957 140215059400576 basic_session_run_hooks.py:692] global_step/sec: 0.741872\n", | |
"INFO:tensorflow:loss = 0.4634264, step = 2600 (134.794 sec)\n", | |
"I0503 23:54:57.377939 140215059400576 basic_session_run_hooks.py:260] loss = 0.4634264, step = 2600 (134.794 sec)\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:Saving checkpoints for 2627 into /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt.\n", | |
"I0503 23:55:32.360968 140215059400576 basic_session_run_hooks.py:606] Saving checkpoints for 2627 into /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt.\n", | |
"INFO:tensorflow:Reading unweighted datasets: ['/content/data/tf_record/pet_faces_train.record-?????-of-00010']\n", | |
"I0503 23:55:34.447839 140215059400576 dataset_builder.py:163] Reading unweighted datasets: ['/content/data/tf_record/pet_faces_train.record-?????-of-00010']\n", | |
"INFO:tensorflow:Reading record datasets for input file: ['/content/data/tf_record/pet_faces_train.record-?????-of-00010']\n", | |
"I0503 23:55:34.448860 140215059400576 dataset_builder.py:80] Reading record datasets for input file: ['/content/data/tf_record/pet_faces_train.record-?????-of-00010']\n", | |
"INFO:tensorflow:Number of filenames to read: 10\n", | |
"I0503 23:55:34.448984 140215059400576 dataset_builder.py:81] Number of filenames to read: 10\n", | |
"WARNING:tensorflow:Entity <bound method TfExampleDecoder.decode of <object_detection.data_decoders.tf_example_decoder.TfExampleDecoder object at 0x7f8536566e90>> could not be transformed and will be executed as-is. Please report this to the AutoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: module 'gast' has no attribute 'Num'\n", | |
"W0503 23:55:34.486161 140215059400576 ag_logging.py:146] Entity <bound method TfExampleDecoder.decode of <object_detection.data_decoders.tf_example_decoder.TfExampleDecoder object at 0x7f8536566e90>> could not be transformed and will be executed as-is. Please report this to the AutoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: module 'gast' has no attribute 'Num'\n", | |
"WARNING:tensorflow:Entity <function eval_input.<locals>.transform_and_pad_input_data_fn at 0x7f805fc77170> could not be transformed and will be executed as-is. Please report this to the AutoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: Bad argument number for Name: 3, expecting 4\n", | |
"W0503 23:55:34.647705 140215059400576 ag_logging.py:146] Entity <function eval_input.<locals>.transform_and_pad_input_data_fn at 0x7f805fc77170> could not be transformed and will be executed as-is. Please report this to the AutoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: Bad argument number for Name: 3, expecting 4\n", | |
"INFO:tensorflow:Calling model_fn.\n", | |
"I0503 23:55:35.134610 140215059400576 estimator.py:1148] Calling model_fn.\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0503 23:55:37.151859 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0503 23:55:37.220672 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0503 23:55:37.288931 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0503 23:55:37.356766 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0503 23:55:37.427735 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0503 23:55:37.498246 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:Done calling model_fn.\n", | |
"I0503 23:55:39.250312 140215059400576 estimator.py:1150] Done calling model_fn.\n", | |
"INFO:tensorflow:Starting evaluation at 2021-05-03T23:55:39Z\n", | |
"I0503 23:55:39.264970 140215059400576 evaluation.py:255] Starting evaluation at 2021-05-03T23:55:39Z\n", | |
"INFO:tensorflow:Graph was finalized.\n", | |
"I0503 23:55:39.714994 140215059400576 monitored_session.py:240] Graph was finalized.\n", | |
"2021-05-03 23:55:39.715629: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-03 23:55:39.715926: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1639] Found device 0 with properties: \n", | |
"name: Tesla P100-PCIE-16GB major: 6 minor: 0 memoryClockRate(GHz): 1.3285\n", | |
"pciBusID: 0000:00:04.0\n", | |
"2021-05-03 23:55:39.716017: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.1\n", | |
"2021-05-03 23:55:39.716039: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcublas.so.10\n", | |
"2021-05-03 23:55:39.716054: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcufft.so.10\n", | |
"2021-05-03 23:55:39.716071: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcurand.so.10\n", | |
"2021-05-03 23:55:39.716099: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusolver.so.10\n", | |
"2021-05-03 23:55:39.716118: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusparse.so.10\n", | |
"2021-05-03 23:55:39.716130: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudnn.so.7\n", | |
"2021-05-03 23:55:39.716193: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-03 23:55:39.716434: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-03 23:55:39.716623: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1767] Adding visible gpu devices: 0\n", | |
"2021-05-03 23:55:39.716697: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1180] Device interconnect StreamExecutor with strength 1 edge matrix:\n", | |
"2021-05-03 23:55:39.716709: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1186] 0 \n", | |
"2021-05-03 23:55:39.716715: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1199] 0: N \n", | |
"2021-05-03 23:55:39.716795: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-03 23:55:39.717024: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-03 23:55:39.717234: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1325] Created TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 15224 MB memory) -> physical GPU (device: 0, name: Tesla P100-PCIE-16GB, pci bus id: 0000:00:04.0, compute capability: 6.0)\n", | |
"INFO:tensorflow:Restoring parameters from /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt-2627\n", | |
"I0503 23:55:39.718320 140215059400576 saver.py:1284] Restoring parameters from /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt-2627\n", | |
"INFO:tensorflow:Running local_init_op.\n", | |
"I0503 23:55:40.710749 140215059400576 session_manager.py:500] Running local_init_op.\n", | |
"INFO:tensorflow:Done running local_init_op.\n", | |
"I0503 23:55:40.846814 140215059400576 session_manager.py:502] Done running local_init_op.\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:Performing evaluation on 2568 images.\n", | |
"I0503 23:56:46.608262 140210432521984 coco_evaluation.py:293] Performing evaluation on 2568 images.\n", | |
"creating index...\n", | |
"index created!\n", | |
"INFO:tensorflow:Loading and preparing annotation results...\n", | |
"I0503 23:56:46.615258 140210432521984 coco_tools.py:116] Loading and preparing annotation results...\n", | |
"INFO:tensorflow:DONE (t=0.19s)\n", | |
"I0503 23:56:46.803236 140210432521984 coco_tools.py:138] DONE (t=0.19s)\n", | |
"creating index...\n", | |
"index created!\n", | |
"Running per image evaluation...\n", | |
"Evaluate annotation type *bbox*\n", | |
"DONE (t=19.62s).\n", | |
"Accumulating evaluation results...\n", | |
"DONE (t=6.87s).\n", | |
" Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.767\n", | |
" Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=100 ] = 0.901\n", | |
" Average Precision (AP) @[ IoU=0.75 | area= all | maxDets=100 ] = 0.887\n", | |
" Average Precision (AP) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.003\n", | |
" Average Precision (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.309\n", | |
" Average Precision (AP) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.826\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 1 ] = 0.860\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 10 ] = 0.862\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.862\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.200\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.740\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.883\n", | |
"INFO:tensorflow:Finished evaluation at 2021-05-03-23:57:14\n", | |
"I0503 23:57:14.283967 140215059400576 evaluation.py:275] Finished evaluation at 2021-05-03-23:57:14\n", | |
"INFO:tensorflow:Saving dict for global step 2627: DetectionBoxes_Precision/mAP = 0.76652586, DetectionBoxes_Precision/mAP (large) = 0.82630664, DetectionBoxes_Precision/mAP (medium) = 0.30866796, DetectionBoxes_Precision/mAP (small) = 0.0033663367, DetectionBoxes_Precision/[email protected] = 0.9011963, DetectionBoxes_Precision/[email protected] = 0.88666576, DetectionBoxes_Recall/AR@1 = 0.85985476, DetectionBoxes_Recall/AR@10 = 0.8622456, DetectionBoxes_Recall/AR@100 = 0.8622456, DetectionBoxes_Recall/AR@100 (large) = 0.8833723, DetectionBoxes_Recall/AR@100 (medium) = 0.74026436, DetectionBoxes_Recall/AR@100 (small) = 0.2, Loss/classification_loss = 0.2612513, Loss/localization_loss = 0.031032389, Loss/regularization_loss = 0.22521982, Loss/total_loss = 0.5175049, global_step = 2627, learning_rate = 0.039999757, loss = 0.5175049\n", | |
"I0503 23:57:14.284255 140215059400576 estimator.py:2049] Saving dict for global step 2627: DetectionBoxes_Precision/mAP = 0.76652586, DetectionBoxes_Precision/mAP (large) = 0.82630664, DetectionBoxes_Precision/mAP (medium) = 0.30866796, DetectionBoxes_Precision/mAP (small) = 0.0033663367, DetectionBoxes_Precision/[email protected] = 0.9011963, DetectionBoxes_Precision/[email protected] = 0.88666576, DetectionBoxes_Recall/AR@1 = 0.85985476, DetectionBoxes_Recall/AR@10 = 0.8622456, DetectionBoxes_Recall/AR@100 = 0.8622456, DetectionBoxes_Recall/AR@100 (large) = 0.8833723, DetectionBoxes_Recall/AR@100 (medium) = 0.74026436, DetectionBoxes_Recall/AR@100 (small) = 0.2, Loss/classification_loss = 0.2612513, Loss/localization_loss = 0.031032389, Loss/regularization_loss = 0.22521982, Loss/total_loss = 0.5175049, global_step = 2627, learning_rate = 0.039999757, loss = 0.5175049\n", | |
"INFO:tensorflow:Saving 'checkpoint_path' summary for global step 2627: /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt-2627\n", | |
"I0503 23:57:14.287927 140215059400576 estimator.py:2109] Saving 'checkpoint_path' summary for global step 2627: /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt-2627\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:global_step/sec: 0.423392\n", | |
"I0503 23:58:53.564939 140215059400576 basic_session_run_hooks.py:692] global_step/sec: 0.423392\n", | |
"INFO:tensorflow:loss = 0.44238347, step = 2700 (236.188 sec)\n", | |
"I0503 23:58:53.566258 140215059400576 basic_session_run_hooks.py:260] loss = 0.44238347, step = 2700 (236.188 sec)\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:global_step/sec: 0.738639\n", | |
"I0504 00:01:08.949173 140215059400576 basic_session_run_hooks.py:692] global_step/sec: 0.738639\n", | |
"INFO:tensorflow:loss = 0.44358426, step = 2800 (135.384 sec)\n", | |
"I0504 00:01:08.950550 140215059400576 basic_session_run_hooks.py:260] loss = 0.44358426, step = 2800 (135.384 sec)\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:global_step/sec: 0.739956\n", | |
"I0504 00:03:24.092235 140215059400576 basic_session_run_hooks.py:692] global_step/sec: 0.739956\n", | |
"INFO:tensorflow:loss = 0.4026372, step = 2900 (135.143 sec)\n", | |
"I0504 00:03:24.093274 140215059400576 basic_session_run_hooks.py:260] loss = 0.4026372, step = 2900 (135.143 sec)\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:Saving checkpoints for 2996 into /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt.\n", | |
"I0504 00:05:32.992605 140215059400576 basic_session_run_hooks.py:606] Saving checkpoints for 2996 into /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt.\n", | |
"INFO:tensorflow:Reading unweighted datasets: ['/content/data/tf_record/pet_faces_train.record-?????-of-00010']\n", | |
"I0504 00:05:35.185099 140215059400576 dataset_builder.py:163] Reading unweighted datasets: ['/content/data/tf_record/pet_faces_train.record-?????-of-00010']\n", | |
"INFO:tensorflow:Reading record datasets for input file: ['/content/data/tf_record/pet_faces_train.record-?????-of-00010']\n", | |
"I0504 00:05:35.186123 140215059400576 dataset_builder.py:80] Reading record datasets for input file: ['/content/data/tf_record/pet_faces_train.record-?????-of-00010']\n", | |
"INFO:tensorflow:Number of filenames to read: 10\n", | |
"I0504 00:05:35.186289 140215059400576 dataset_builder.py:81] Number of filenames to read: 10\n", | |
"WARNING:tensorflow:Entity <bound method TfExampleDecoder.decode of <object_detection.data_decoders.tf_example_decoder.TfExampleDecoder object at 0x7f85c66c8410>> could not be transformed and will be executed as-is. Please report this to the AutoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: module 'gast' has no attribute 'Num'\n", | |
"W0504 00:05:35.223627 140215059400576 ag_logging.py:146] Entity <bound method TfExampleDecoder.decode of <object_detection.data_decoders.tf_example_decoder.TfExampleDecoder object at 0x7f85c66c8410>> could not be transformed and will be executed as-is. Please report this to the AutoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: module 'gast' has no attribute 'Num'\n", | |
"WARNING:tensorflow:Entity <function eval_input.<locals>.transform_and_pad_input_data_fn at 0x7f85ba63fa70> could not be transformed and will be executed as-is. Please report this to the AutoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: Bad argument number for Name: 3, expecting 4\n", | |
"W0504 00:05:35.383057 140215059400576 ag_logging.py:146] Entity <function eval_input.<locals>.transform_and_pad_input_data_fn at 0x7f85ba63fa70> could not be transformed and will be executed as-is. Please report this to the AutoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: Bad argument number for Name: 3, expecting 4\n", | |
"INFO:tensorflow:Calling model_fn.\n", | |
"I0504 00:05:35.860261 140215059400576 estimator.py:1148] Calling model_fn.\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0504 00:05:37.884728 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0504 00:05:37.954850 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0504 00:05:38.026676 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0504 00:05:38.096376 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0504 00:05:38.166676 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0504 00:05:38.238041 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:Done calling model_fn.\n", | |
"I0504 00:05:39.966716 140215059400576 estimator.py:1150] Done calling model_fn.\n", | |
"INFO:tensorflow:Starting evaluation at 2021-05-04T00:05:39Z\n", | |
"I0504 00:05:39.981636 140215059400576 evaluation.py:255] Starting evaluation at 2021-05-04T00:05:39Z\n", | |
"INFO:tensorflow:Graph was finalized.\n", | |
"I0504 00:05:40.430765 140215059400576 monitored_session.py:240] Graph was finalized.\n", | |
"2021-05-04 00:05:40.431482: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-04 00:05:40.431842: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1639] Found device 0 with properties: \n", | |
"name: Tesla P100-PCIE-16GB major: 6 minor: 0 memoryClockRate(GHz): 1.3285\n", | |
"pciBusID: 0000:00:04.0\n", | |
"2021-05-04 00:05:40.431951: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.1\n", | |
"2021-05-04 00:05:40.431975: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcublas.so.10\n", | |
"2021-05-04 00:05:40.431991: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcufft.so.10\n", | |
"2021-05-04 00:05:40.432008: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcurand.so.10\n", | |
"2021-05-04 00:05:40.432033: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusolver.so.10\n", | |
"2021-05-04 00:05:40.432046: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusparse.so.10\n", | |
"2021-05-04 00:05:40.432061: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudnn.so.7\n", | |
"2021-05-04 00:05:40.432152: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-04 00:05:40.432400: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-04 00:05:40.432589: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1767] Adding visible gpu devices: 0\n", | |
"2021-05-04 00:05:40.432628: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1180] Device interconnect StreamExecutor with strength 1 edge matrix:\n", | |
"2021-05-04 00:05:40.432639: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1186] 0 \n", | |
"2021-05-04 00:05:40.432654: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1199] 0: N \n", | |
"2021-05-04 00:05:40.432742: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-04 00:05:40.432973: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-04 00:05:40.433184: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1325] Created TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 15224 MB memory) -> physical GPU (device: 0, name: Tesla P100-PCIE-16GB, pci bus id: 0000:00:04.0, compute capability: 6.0)\n", | |
"INFO:tensorflow:Restoring parameters from /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt-2996\n", | |
"I0504 00:05:40.434189 140215059400576 saver.py:1284] Restoring parameters from /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt-2996\n", | |
"INFO:tensorflow:Running local_init_op.\n", | |
"I0504 00:05:41.416208 140215059400576 session_manager.py:500] Running local_init_op.\n", | |
"INFO:tensorflow:Done running local_init_op.\n", | |
"I0504 00:05:41.550564 140215059400576 session_manager.py:502] Done running local_init_op.\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:Performing evaluation on 2568 images.\n", | |
"I0504 00:06:46.620033 140210432521984 coco_evaluation.py:293] Performing evaluation on 2568 images.\n", | |
"creating index...\n", | |
"index created!\n", | |
"INFO:tensorflow:Loading and preparing annotation results...\n", | |
"I0504 00:06:46.625624 140210432521984 coco_tools.py:116] Loading and preparing annotation results...\n", | |
"INFO:tensorflow:DONE (t=0.21s)\n", | |
"I0504 00:06:46.832377 140210432521984 coco_tools.py:138] DONE (t=0.21s)\n", | |
"creating index...\n", | |
"index created!\n", | |
"Running per image evaluation...\n", | |
"Evaluate annotation type *bbox*\n", | |
"DONE (t=19.79s).\n", | |
"Accumulating evaluation results...\n", | |
"DONE (t=6.77s).\n", | |
" Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.773\n", | |
" Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=100 ] = 0.920\n", | |
" Average Precision (AP) @[ IoU=0.75 | area= all | maxDets=100 ] = 0.903\n", | |
" Average Precision (AP) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.001\n", | |
" Average Precision (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.322\n", | |
" Average Precision (AP) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.830\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 1 ] = 0.851\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 10 ] = 0.855\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.855\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.100\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.730\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.877\n", | |
"INFO:tensorflow:Finished evaluation at 2021-05-04-00:07:14\n", | |
"I0504 00:07:14.388152 140215059400576 evaluation.py:275] Finished evaluation at 2021-05-04-00:07:14\n", | |
"INFO:tensorflow:Saving dict for global step 2996: DetectionBoxes_Precision/mAP = 0.77297103, DetectionBoxes_Precision/mAP (large) = 0.82952887, DetectionBoxes_Precision/mAP (medium) = 0.3219404, DetectionBoxes_Precision/mAP (small) = 0.00050495047, DetectionBoxes_Precision/[email protected] = 0.9201254, DetectionBoxes_Precision/[email protected] = 0.903216, DetectionBoxes_Recall/AR@1 = 0.8508792, DetectionBoxes_Recall/AR@10 = 0.85464895, DetectionBoxes_Recall/AR@100 = 0.85472006, DetectionBoxes_Recall/AR@100 (large) = 0.8771587, DetectionBoxes_Recall/AR@100 (medium) = 0.7297572, DetectionBoxes_Recall/AR@100 (small) = 0.1, Loss/classification_loss = 0.2320339, Loss/localization_loss = 0.033271253, Loss/regularization_loss = 0.22389893, Loss/total_loss = 0.4892042, global_step = 2996, learning_rate = 0.03999938, loss = 0.4892042\n", | |
"I0504 00:07:14.388409 140215059400576 estimator.py:2049] Saving dict for global step 2996: DetectionBoxes_Precision/mAP = 0.77297103, DetectionBoxes_Precision/mAP (large) = 0.82952887, DetectionBoxes_Precision/mAP (medium) = 0.3219404, DetectionBoxes_Precision/mAP (small) = 0.00050495047, DetectionBoxes_Precision/[email protected] = 0.9201254, DetectionBoxes_Precision/[email protected] = 0.903216, DetectionBoxes_Recall/AR@1 = 0.8508792, DetectionBoxes_Recall/AR@10 = 0.85464895, DetectionBoxes_Recall/AR@100 = 0.85472006, DetectionBoxes_Recall/AR@100 (large) = 0.8771587, DetectionBoxes_Recall/AR@100 (medium) = 0.7297572, DetectionBoxes_Recall/AR@100 (small) = 0.1, Loss/classification_loss = 0.2320339, Loss/localization_loss = 0.033271253, Loss/regularization_loss = 0.22389893, Loss/total_loss = 0.4892042, global_step = 2996, learning_rate = 0.03999938, loss = 0.4892042\n", | |
"INFO:tensorflow:Saving 'checkpoint_path' summary for global step 2996: /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt-2996\n", | |
"I0504 00:07:14.392197 140215059400576 estimator.py:2109] Saving 'checkpoint_path' summary for global step 2996: /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt-2996\n", | |
"INFO:tensorflow:global_step/sec: 0.421709\n", | |
"I0504 00:07:21.222511 140215059400576 basic_session_run_hooks.py:692] global_step/sec: 0.421709\n", | |
"INFO:tensorflow:loss = 0.40506738, step = 3000 (237.130 sec)\n", | |
"I0504 00:07:21.223703 140215059400576 basic_session_run_hooks.py:260] loss = 0.40506738, step = 3000 (237.130 sec)\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:global_step/sec: 0.738826\n", | |
"I0504 00:09:36.572391 140215059400576 basic_session_run_hooks.py:692] global_step/sec: 0.738826\n", | |
"INFO:tensorflow:loss = 0.4035138, step = 3100 (135.350 sec)\n", | |
"I0504 00:09:36.573441 140215059400576 basic_session_run_hooks.py:260] loss = 0.4035138, step = 3100 (135.350 sec)\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:global_step/sec: 0.742076\n", | |
"I0504 00:11:51.329473 140215059400576 basic_session_run_hooks.py:692] global_step/sec: 0.742076\n", | |
"INFO:tensorflow:loss = 0.3989345, step = 3200 (134.757 sec)\n", | |
"I0504 00:11:51.330450 140215059400576 basic_session_run_hooks.py:260] loss = 0.3989345, step = 3200 (134.757 sec)\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:global_step/sec: 0.740713\n", | |
"I0504 00:14:06.334585 140215059400576 basic_session_run_hooks.py:692] global_step/sec: 0.740713\n", | |
"INFO:tensorflow:loss = 0.39785632, step = 3300 (135.005 sec)\n", | |
"I0504 00:14:06.335711 140215059400576 basic_session_run_hooks.py:260] loss = 0.39785632, step = 3300 (135.005 sec)\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:Saving checkpoints for 3366 into /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt.\n", | |
"I0504 00:15:33.556672 140215059400576 basic_session_run_hooks.py:606] Saving checkpoints for 3366 into /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt.\n", | |
"INFO:tensorflow:Reading unweighted datasets: ['/content/data/tf_record/pet_faces_train.record-?????-of-00010']\n", | |
"I0504 00:15:35.672658 140215059400576 dataset_builder.py:163] Reading unweighted datasets: ['/content/data/tf_record/pet_faces_train.record-?????-of-00010']\n", | |
"INFO:tensorflow:Reading record datasets for input file: ['/content/data/tf_record/pet_faces_train.record-?????-of-00010']\n", | |
"I0504 00:15:35.673603 140215059400576 dataset_builder.py:80] Reading record datasets for input file: ['/content/data/tf_record/pet_faces_train.record-?????-of-00010']\n", | |
"INFO:tensorflow:Number of filenames to read: 10\n", | |
"I0504 00:15:35.673736 140215059400576 dataset_builder.py:81] Number of filenames to read: 10\n", | |
"WARNING:tensorflow:Entity <bound method TfExampleDecoder.decode of <object_detection.data_decoders.tf_example_decoder.TfExampleDecoder object at 0x7f8061a0bf50>> could not be transformed and will be executed as-is. Please report this to the AutoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: module 'gast' has no attribute 'Num'\n", | |
"W0504 00:15:35.711191 140215059400576 ag_logging.py:146] Entity <bound method TfExampleDecoder.decode of <object_detection.data_decoders.tf_example_decoder.TfExampleDecoder object at 0x7f8061a0bf50>> could not be transformed and will be executed as-is. Please report this to the AutoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: module 'gast' has no attribute 'Num'\n", | |
"WARNING:tensorflow:Entity <function eval_input.<locals>.transform_and_pad_input_data_fn at 0x7f805cd06d40> could not be transformed and will be executed as-is. Please report this to the AutoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: Bad argument number for Name: 3, expecting 4\n", | |
"W0504 00:15:35.870846 140215059400576 ag_logging.py:146] Entity <function eval_input.<locals>.transform_and_pad_input_data_fn at 0x7f805cd06d40> could not be transformed and will be executed as-is. Please report this to the AutoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: Bad argument number for Name: 3, expecting 4\n", | |
"INFO:tensorflow:Calling model_fn.\n", | |
"I0504 00:15:36.724589 140215059400576 estimator.py:1148] Calling model_fn.\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0504 00:15:38.753186 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0504 00:15:38.822665 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0504 00:15:38.899943 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0504 00:15:38.970214 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0504 00:15:39.044627 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0504 00:15:39.116199 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:Done calling model_fn.\n", | |
"I0504 00:15:40.858367 140215059400576 estimator.py:1150] Done calling model_fn.\n", | |
"INFO:tensorflow:Starting evaluation at 2021-05-04T00:15:40Z\n", | |
"I0504 00:15:40.873203 140215059400576 evaluation.py:255] Starting evaluation at 2021-05-04T00:15:40Z\n", | |
"INFO:tensorflow:Graph was finalized.\n", | |
"I0504 00:15:41.351674 140215059400576 monitored_session.py:240] Graph was finalized.\n", | |
"2021-05-04 00:15:41.352276: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-04 00:15:41.352571: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1639] Found device 0 with properties: \n", | |
"name: Tesla P100-PCIE-16GB major: 6 minor: 0 memoryClockRate(GHz): 1.3285\n", | |
"pciBusID: 0000:00:04.0\n", | |
"2021-05-04 00:15:41.352665: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.1\n", | |
"2021-05-04 00:15:41.352688: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcublas.so.10\n", | |
"2021-05-04 00:15:41.352702: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcufft.so.10\n", | |
"2021-05-04 00:15:41.352720: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcurand.so.10\n", | |
"2021-05-04 00:15:41.352739: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusolver.so.10\n", | |
"2021-05-04 00:15:41.352752: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusparse.so.10\n", | |
"2021-05-04 00:15:41.352767: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudnn.so.7\n", | |
"2021-05-04 00:15:41.352834: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-04 00:15:41.353077: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-04 00:15:41.353303: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1767] Adding visible gpu devices: 0\n", | |
"2021-05-04 00:15:41.353392: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1180] Device interconnect StreamExecutor with strength 1 edge matrix:\n", | |
"2021-05-04 00:15:41.353408: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1186] 0 \n", | |
"2021-05-04 00:15:41.353417: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1199] 0: N \n", | |
"2021-05-04 00:15:41.353534: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-04 00:15:41.353810: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-04 00:15:41.354072: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1325] Created TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 15224 MB memory) -> physical GPU (device: 0, name: Tesla P100-PCIE-16GB, pci bus id: 0000:00:04.0, compute capability: 6.0)\n", | |
"INFO:tensorflow:Restoring parameters from /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt-3366\n", | |
"I0504 00:15:41.355482 140215059400576 saver.py:1284] Restoring parameters from /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt-3366\n", | |
"INFO:tensorflow:Running local_init_op.\n", | |
"I0504 00:15:42.373521 140215059400576 session_manager.py:500] Running local_init_op.\n", | |
"INFO:tensorflow:Done running local_init_op.\n", | |
"I0504 00:15:42.529315 140215059400576 session_manager.py:502] Done running local_init_op.\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:Performing evaluation on 2568 images.\n", | |
"I0504 00:16:48.795337 140210424129280 coco_evaluation.py:293] Performing evaluation on 2568 images.\n", | |
"creating index...\n", | |
"index created!\n", | |
"INFO:tensorflow:Loading and preparing annotation results...\n", | |
"I0504 00:16:48.813607 140210424129280 coco_tools.py:116] Loading and preparing annotation results...\n", | |
"INFO:tensorflow:DONE (t=0.21s)\n", | |
"I0504 00:16:49.026471 140210424129280 coco_tools.py:138] DONE (t=0.21s)\n", | |
"creating index...\n", | |
"index created!\n", | |
"Running per image evaluation...\n", | |
"Evaluate annotation type *bbox*\n", | |
"DONE (t=19.41s).\n", | |
"Accumulating evaluation results...\n", | |
"DONE (t=7.61s).\n", | |
" Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.714\n", | |
" Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=100 ] = 0.885\n", | |
" Average Precision (AP) @[ IoU=0.75 | area= all | maxDets=100 ] = 0.843\n", | |
" Average Precision (AP) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.001\n", | |
" Average Precision (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.307\n", | |
" Average Precision (AP) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.768\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 1 ] = 0.815\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 10 ] = 0.827\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.827\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.100\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.698\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.852\n", | |
"INFO:tensorflow:Finished evaluation at 2021-05-04-00:17:17\n", | |
"I0504 00:17:17.009499 140215059400576 evaluation.py:275] Finished evaluation at 2021-05-04-00:17:17\n", | |
"INFO:tensorflow:Saving dict for global step 3366: DetectionBoxes_Precision/mAP = 0.7135667, DetectionBoxes_Precision/mAP (large) = 0.7683116, DetectionBoxes_Precision/mAP (medium) = 0.30692887, DetectionBoxes_Precision/mAP (small) = 0.00068236556, DetectionBoxes_Precision/[email protected] = 0.8847008, DetectionBoxes_Precision/[email protected] = 0.8431751, DetectionBoxes_Recall/AR@1 = 0.8154388, DetectionBoxes_Recall/AR@10 = 0.8272542, DetectionBoxes_Recall/AR@100 = 0.8273293, DetectionBoxes_Recall/AR@100 (large) = 0.8517159, DetectionBoxes_Recall/AR@100 (medium) = 0.6981567, DetectionBoxes_Recall/AR@100 (small) = 0.1, Loss/classification_loss = 0.29136416, Loss/localization_loss = 0.047467694, Loss/regularization_loss = 0.2224056, Loss/total_loss = 0.561232, global_step = 3366, learning_rate = 0.039998837, loss = 0.561232\n", | |
"I0504 00:17:17.009752 140215059400576 estimator.py:2049] Saving dict for global step 3366: DetectionBoxes_Precision/mAP = 0.7135667, DetectionBoxes_Precision/mAP (large) = 0.7683116, DetectionBoxes_Precision/mAP (medium) = 0.30692887, DetectionBoxes_Precision/mAP (small) = 0.00068236556, DetectionBoxes_Precision/[email protected] = 0.8847008, DetectionBoxes_Precision/[email protected] = 0.8431751, DetectionBoxes_Recall/AR@1 = 0.8154388, DetectionBoxes_Recall/AR@10 = 0.8272542, DetectionBoxes_Recall/AR@100 = 0.8273293, DetectionBoxes_Recall/AR@100 (large) = 0.8517159, DetectionBoxes_Recall/AR@100 (medium) = 0.6981567, DetectionBoxes_Recall/AR@100 (small) = 0.1, Loss/classification_loss = 0.29136416, Loss/localization_loss = 0.047467694, Loss/regularization_loss = 0.2224056, Loss/total_loss = 0.561232, global_step = 3366, learning_rate = 0.039998837, loss = 0.561232\n", | |
"INFO:tensorflow:Saving 'checkpoint_path' summary for global step 3366: /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt-3366\n", | |
"I0504 00:17:17.013588 140215059400576 estimator.py:2109] Saving 'checkpoint_path' summary for global step 3366: /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt-3366\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:global_step/sec: 0.419808\n", | |
"I0504 00:18:04.538462 140215059400576 basic_session_run_hooks.py:692] global_step/sec: 0.419808\n", | |
"INFO:tensorflow:loss = 0.37604678, step = 3400 (238.204 sec)\n", | |
"I0504 00:18:04.539533 140215059400576 basic_session_run_hooks.py:260] loss = 0.37604678, step = 3400 (238.204 sec)\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:global_step/sec: 0.736851\n", | |
"I0504 00:20:20.251132 140215059400576 basic_session_run_hooks.py:692] global_step/sec: 0.736851\n", | |
"INFO:tensorflow:loss = 0.37406266, step = 3500 (135.713 sec)\n", | |
"I0504 00:20:20.252358 140215059400576 basic_session_run_hooks.py:260] loss = 0.37406266, step = 3500 (135.713 sec)\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:global_step/sec: 0.742703\n", | |
"I0504 00:22:34.894427 140215059400576 basic_session_run_hooks.py:692] global_step/sec: 0.742703\n", | |
"INFO:tensorflow:loss = 0.39673847, step = 3600 (134.643 sec)\n", | |
"I0504 00:22:34.895339 140215059400576 basic_session_run_hooks.py:260] loss = 0.39673847, step = 3600 (134.643 sec)\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:global_step/sec: 0.741718\n", | |
"I0504 00:24:49.716670 140215059400576 basic_session_run_hooks.py:692] global_step/sec: 0.741718\n", | |
"INFO:tensorflow:loss = 0.39229053, step = 3700 (134.822 sec)\n", | |
"I0504 00:24:49.717634 140215059400576 basic_session_run_hooks.py:260] loss = 0.39229053, step = 3700 (134.822 sec)\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:Saving checkpoints for 3734 into /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt.\n", | |
"I0504 00:25:34.346060 140215059400576 basic_session_run_hooks.py:606] Saving checkpoints for 3734 into /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt.\n", | |
"INFO:tensorflow:Reading unweighted datasets: ['/content/data/tf_record/pet_faces_train.record-?????-of-00010']\n", | |
"I0504 00:25:36.445817 140215059400576 dataset_builder.py:163] Reading unweighted datasets: ['/content/data/tf_record/pet_faces_train.record-?????-of-00010']\n", | |
"INFO:tensorflow:Reading record datasets for input file: ['/content/data/tf_record/pet_faces_train.record-?????-of-00010']\n", | |
"I0504 00:25:36.446804 140215059400576 dataset_builder.py:80] Reading record datasets for input file: ['/content/data/tf_record/pet_faces_train.record-?????-of-00010']\n", | |
"INFO:tensorflow:Number of filenames to read: 10\n", | |
"I0504 00:25:36.446929 140215059400576 dataset_builder.py:81] Number of filenames to read: 10\n", | |
"WARNING:tensorflow:Entity <bound method TfExampleDecoder.decode of <object_detection.data_decoders.tf_example_decoder.TfExampleDecoder object at 0x7f8047904f10>> could not be transformed and will be executed as-is. Please report this to the AutoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: module 'gast' has no attribute 'Num'\n", | |
"W0504 00:25:36.484265 140215059400576 ag_logging.py:146] Entity <bound method TfExampleDecoder.decode of <object_detection.data_decoders.tf_example_decoder.TfExampleDecoder object at 0x7f8047904f10>> could not be transformed and will be executed as-is. Please report this to the AutoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: module 'gast' has no attribute 'Num'\n", | |
"WARNING:tensorflow:Entity <function eval_input.<locals>.transform_and_pad_input_data_fn at 0x7f85285340e0> could not be transformed and will be executed as-is. Please report this to the AutoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: Bad argument number for Name: 3, expecting 4\n", | |
"W0504 00:25:36.645600 140215059400576 ag_logging.py:146] Entity <function eval_input.<locals>.transform_and_pad_input_data_fn at 0x7f85285340e0> could not be transformed and will be executed as-is. Please report this to the AutoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: Bad argument number for Name: 3, expecting 4\n", | |
"INFO:tensorflow:Calling model_fn.\n", | |
"I0504 00:25:37.124161 140215059400576 estimator.py:1148] Calling model_fn.\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0504 00:25:39.123061 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0504 00:25:39.192206 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0504 00:25:39.262681 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0504 00:25:39.331016 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0504 00:25:39.399062 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0504 00:25:39.472804 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:Done calling model_fn.\n", | |
"I0504 00:25:41.188759 140215059400576 estimator.py:1150] Done calling model_fn.\n", | |
"INFO:tensorflow:Starting evaluation at 2021-05-04T00:25:41Z\n", | |
"I0504 00:25:41.203554 140215059400576 evaluation.py:255] Starting evaluation at 2021-05-04T00:25:41Z\n", | |
"INFO:tensorflow:Graph was finalized.\n", | |
"I0504 00:25:41.659526 140215059400576 monitored_session.py:240] Graph was finalized.\n", | |
"2021-05-04 00:25:41.660275: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-04 00:25:41.660651: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1639] Found device 0 with properties: \n", | |
"name: Tesla P100-PCIE-16GB major: 6 minor: 0 memoryClockRate(GHz): 1.3285\n", | |
"pciBusID: 0000:00:04.0\n", | |
"2021-05-04 00:25:41.660769: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.1\n", | |
"2021-05-04 00:25:41.660802: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcublas.so.10\n", | |
"2021-05-04 00:25:41.660827: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcufft.so.10\n", | |
"2021-05-04 00:25:41.660851: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcurand.so.10\n", | |
"2021-05-04 00:25:41.660874: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusolver.so.10\n", | |
"2021-05-04 00:25:41.660895: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusparse.so.10\n", | |
"2021-05-04 00:25:41.660917: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudnn.so.7\n", | |
"2021-05-04 00:25:41.661007: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-04 00:25:41.661342: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-04 00:25:41.661565: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1767] Adding visible gpu devices: 0\n", | |
"2021-05-04 00:25:41.661610: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1180] Device interconnect StreamExecutor with strength 1 edge matrix:\n", | |
"2021-05-04 00:25:41.661623: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1186] 0 \n", | |
"2021-05-04 00:25:41.661633: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1199] 0: N \n", | |
"2021-05-04 00:25:41.661729: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-04 00:25:41.662015: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-04 00:25:41.662275: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1325] Created TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 15224 MB memory) -> physical GPU (device: 0, name: Tesla P100-PCIE-16GB, pci bus id: 0000:00:04.0, compute capability: 6.0)\n", | |
"INFO:tensorflow:Restoring parameters from /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt-3734\n", | |
"I0504 00:25:41.663229 140215059400576 saver.py:1284] Restoring parameters from /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt-3734\n", | |
"INFO:tensorflow:Running local_init_op.\n", | |
"I0504 00:25:42.661723 140215059400576 session_manager.py:500] Running local_init_op.\n", | |
"INFO:tensorflow:Done running local_init_op.\n", | |
"I0504 00:25:42.803506 140215059400576 session_manager.py:502] Done running local_init_op.\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:Performing evaluation on 2568 images.\n", | |
"I0504 00:26:48.375295 140210415736576 coco_evaluation.py:293] Performing evaluation on 2568 images.\n", | |
"creating index...\n", | |
"index created!\n", | |
"INFO:tensorflow:Loading and preparing annotation results...\n", | |
"I0504 00:26:48.382606 140210415736576 coco_tools.py:116] Loading and preparing annotation results...\n", | |
"INFO:tensorflow:DONE (t=0.19s)\n", | |
"I0504 00:26:48.575759 140210415736576 coco_tools.py:138] DONE (t=0.19s)\n", | |
"creating index...\n", | |
"index created!\n", | |
"Running per image evaluation...\n", | |
"Evaluate annotation type *bbox*\n", | |
"DONE (t=19.74s).\n", | |
"Accumulating evaluation results...\n", | |
"DONE (t=6.98s).\n", | |
" Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.834\n", | |
" Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=100 ] = 0.957\n", | |
" Average Precision (AP) @[ IoU=0.75 | area= all | maxDets=100 ] = 0.947\n", | |
" Average Precision (AP) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.006\n", | |
" Average Precision (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.457\n", | |
" Average Precision (AP) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.874\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 1 ] = 0.885\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 10 ] = 0.889\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.889\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.400\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.782\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.907\n", | |
"INFO:tensorflow:Finished evaluation at 2021-05-04-00:27:16\n", | |
"I0504 00:27:16.270267 140215059400576 evaluation.py:275] Finished evaluation at 2021-05-04-00:27:16\n", | |
"INFO:tensorflow:Saving dict for global step 3734: DetectionBoxes_Precision/mAP = 0.83393395, DetectionBoxes_Precision/mAP (large) = 0.8735613, DetectionBoxes_Precision/mAP (medium) = 0.4568373, DetectionBoxes_Precision/mAP (small) = 0.006100533, DetectionBoxes_Precision/[email protected] = 0.9569519, DetectionBoxes_Precision/[email protected] = 0.9465632, DetectionBoxes_Recall/AR@1 = 0.8853066, DetectionBoxes_Recall/AR@10 = 0.8890051, DetectionBoxes_Recall/AR@100 = 0.8890051, DetectionBoxes_Recall/AR@100 (large) = 0.90670025, DetectionBoxes_Recall/AR@100 (medium) = 0.7820043, DetectionBoxes_Recall/AR@100 (small) = 0.4, Loss/classification_loss = 0.17915349, Loss/localization_loss = 0.023984391, Loss/regularization_loss = 0.22084594, Loss/total_loss = 0.42398, global_step = 3734, learning_rate = 0.039998125, loss = 0.42398\n", | |
"I0504 00:27:16.270514 140215059400576 estimator.py:2049] Saving dict for global step 3734: DetectionBoxes_Precision/mAP = 0.83393395, DetectionBoxes_Precision/mAP (large) = 0.8735613, DetectionBoxes_Precision/mAP (medium) = 0.4568373, DetectionBoxes_Precision/mAP (small) = 0.006100533, DetectionBoxes_Precision/[email protected] = 0.9569519, DetectionBoxes_Precision/[email protected] = 0.9465632, DetectionBoxes_Recall/AR@1 = 0.8853066, DetectionBoxes_Recall/AR@10 = 0.8890051, DetectionBoxes_Recall/AR@100 = 0.8890051, DetectionBoxes_Recall/AR@100 (large) = 0.90670025, DetectionBoxes_Recall/AR@100 (medium) = 0.7820043, DetectionBoxes_Recall/AR@100 (small) = 0.4, Loss/classification_loss = 0.17915349, Loss/localization_loss = 0.023984391, Loss/regularization_loss = 0.22084594, Loss/total_loss = 0.42398, global_step = 3734, learning_rate = 0.039998125, loss = 0.42398\n", | |
"INFO:tensorflow:Saving 'checkpoint_path' summary for global step 3734: /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt-3734\n", | |
"I0504 00:27:16.274342 140215059400576 estimator.py:2109] Saving 'checkpoint_path' summary for global step 3734: /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt-3734\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:global_step/sec: 0.421316\n", | |
"I0504 00:28:47.068329 140215059400576 basic_session_run_hooks.py:692] global_step/sec: 0.421316\n", | |
"INFO:tensorflow:loss = 0.39515993, step = 3800 (237.352 sec)\n", | |
"I0504 00:28:47.069396 140215059400576 basic_session_run_hooks.py:260] loss = 0.39515993, step = 3800 (237.352 sec)\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:global_step/sec: 0.740153\n", | |
"I0504 00:31:02.175557 140215059400576 basic_session_run_hooks.py:692] global_step/sec: 0.740153\n", | |
"INFO:tensorflow:loss = 0.37139446, step = 3900 (135.107 sec)\n", | |
"I0504 00:31:02.176748 140215059400576 basic_session_run_hooks.py:260] loss = 0.37139446, step = 3900 (135.107 sec)\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:global_step/sec: 0.740383\n", | |
"I0504 00:33:17.240847 140215059400576 basic_session_run_hooks.py:692] global_step/sec: 0.740383\n", | |
"INFO:tensorflow:loss = 0.40161628, step = 4000 (135.065 sec)\n", | |
"I0504 00:33:17.241988 140215059400576 basic_session_run_hooks.py:260] loss = 0.40161628, step = 4000 (135.065 sec)\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:global_step/sec: 0.740292\n", | |
"I0504 00:35:32.322628 140215059400576 basic_session_run_hooks.py:692] global_step/sec: 0.740292\n", | |
"INFO:tensorflow:loss = 0.383332, step = 4100 (135.082 sec)\n", | |
"I0504 00:35:32.323656 140215059400576 basic_session_run_hooks.py:260] loss = 0.383332, step = 4100 (135.082 sec)\n", | |
"INFO:tensorflow:Saving checkpoints for 4103 into /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt.\n", | |
"I0504 00:35:35.057940 140215059400576 basic_session_run_hooks.py:606] Saving checkpoints for 4103 into /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt.\n", | |
"INFO:tensorflow:Reading unweighted datasets: ['/content/data/tf_record/pet_faces_train.record-?????-of-00010']\n", | |
"I0504 00:35:37.166390 140215059400576 dataset_builder.py:163] Reading unweighted datasets: ['/content/data/tf_record/pet_faces_train.record-?????-of-00010']\n", | |
"INFO:tensorflow:Reading record datasets for input file: ['/content/data/tf_record/pet_faces_train.record-?????-of-00010']\n", | |
"I0504 00:35:37.167422 140215059400576 dataset_builder.py:80] Reading record datasets for input file: ['/content/data/tf_record/pet_faces_train.record-?????-of-00010']\n", | |
"INFO:tensorflow:Number of filenames to read: 10\n", | |
"I0504 00:35:37.167551 140215059400576 dataset_builder.py:81] Number of filenames to read: 10\n", | |
"WARNING:tensorflow:Entity <bound method TfExampleDecoder.decode of <object_detection.data_decoders.tf_example_decoder.TfExampleDecoder object at 0x7f8063900f50>> could not be transformed and will be executed as-is. Please report this to the AutoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: module 'gast' has no attribute 'Num'\n", | |
"W0504 00:35:37.204989 140215059400576 ag_logging.py:146] Entity <bound method TfExampleDecoder.decode of <object_detection.data_decoders.tf_example_decoder.TfExampleDecoder object at 0x7f8063900f50>> could not be transformed and will be executed as-is. Please report this to the AutoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: module 'gast' has no attribute 'Num'\n", | |
"WARNING:tensorflow:Entity <function eval_input.<locals>.transform_and_pad_input_data_fn at 0x7f80646d0170> could not be transformed and will be executed as-is. Please report this to the AutoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: Bad argument number for Name: 3, expecting 4\n", | |
"W0504 00:35:37.365764 140215059400576 ag_logging.py:146] Entity <function eval_input.<locals>.transform_and_pad_input_data_fn at 0x7f80646d0170> could not be transformed and will be executed as-is. Please report this to the AutoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: Bad argument number for Name: 3, expecting 4\n", | |
"INFO:tensorflow:Calling model_fn.\n", | |
"I0504 00:35:37.850458 140215059400576 estimator.py:1148] Calling model_fn.\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0504 00:35:39.881611 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0504 00:35:39.950798 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0504 00:35:40.020510 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0504 00:35:40.088593 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0504 00:35:40.159373 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0504 00:35:40.228047 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:Done calling model_fn.\n", | |
"I0504 00:35:41.970594 140215059400576 estimator.py:1150] Done calling model_fn.\n", | |
"INFO:tensorflow:Starting evaluation at 2021-05-04T00:35:41Z\n", | |
"I0504 00:35:41.985589 140215059400576 evaluation.py:255] Starting evaluation at 2021-05-04T00:35:41Z\n", | |
"INFO:tensorflow:Graph was finalized.\n", | |
"I0504 00:35:42.442586 140215059400576 monitored_session.py:240] Graph was finalized.\n", | |
"2021-05-04 00:35:42.443217: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-04 00:35:42.443514: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1639] Found device 0 with properties: \n", | |
"name: Tesla P100-PCIE-16GB major: 6 minor: 0 memoryClockRate(GHz): 1.3285\n", | |
"pciBusID: 0000:00:04.0\n", | |
"2021-05-04 00:35:42.443611: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.1\n", | |
"2021-05-04 00:35:42.443630: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcublas.so.10\n", | |
"2021-05-04 00:35:42.443643: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcufft.so.10\n", | |
"2021-05-04 00:35:42.443658: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcurand.so.10\n", | |
"2021-05-04 00:35:42.443672: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusolver.so.10\n", | |
"2021-05-04 00:35:42.443686: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusparse.so.10\n", | |
"2021-05-04 00:35:42.443700: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudnn.so.7\n", | |
"2021-05-04 00:35:42.443777: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-04 00:35:42.444014: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-04 00:35:42.444218: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1767] Adding visible gpu devices: 0\n", | |
"2021-05-04 00:35:42.444297: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1180] Device interconnect StreamExecutor with strength 1 edge matrix:\n", | |
"2021-05-04 00:35:42.444308: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1186] 0 \n", | |
"2021-05-04 00:35:42.444315: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1199] 0: N \n", | |
"2021-05-04 00:35:42.444407: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-04 00:35:42.444642: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-04 00:35:42.444849: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1325] Created TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 15224 MB memory) -> physical GPU (device: 0, name: Tesla P100-PCIE-16GB, pci bus id: 0000:00:04.0, compute capability: 6.0)\n", | |
"INFO:tensorflow:Restoring parameters from /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt-4103\n", | |
"I0504 00:35:42.448815 140215059400576 saver.py:1284] Restoring parameters from /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt-4103\n", | |
"INFO:tensorflow:Running local_init_op.\n", | |
"I0504 00:35:43.439375 140215059400576 session_manager.py:500] Running local_init_op.\n", | |
"INFO:tensorflow:Done running local_init_op.\n", | |
"I0504 00:35:43.582256 140215059400576 session_manager.py:502] Done running local_init_op.\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:Performing evaluation on 2568 images.\n", | |
"I0504 00:36:50.223706 140210440914688 coco_evaluation.py:293] Performing evaluation on 2568 images.\n", | |
"creating index...\n", | |
"index created!\n", | |
"INFO:tensorflow:Loading and preparing annotation results...\n", | |
"I0504 00:36:50.232547 140210440914688 coco_tools.py:116] Loading and preparing annotation results...\n", | |
"INFO:tensorflow:DONE (t=0.19s)\n", | |
"I0504 00:36:50.418372 140210440914688 coco_tools.py:138] DONE (t=0.19s)\n", | |
"creating index...\n", | |
"index created!\n", | |
"Running per image evaluation...\n", | |
"Evaluate annotation type *bbox*\n", | |
"DONE (t=19.73s).\n", | |
"Accumulating evaluation results...\n", | |
"DONE (t=6.79s).\n", | |
" Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.832\n", | |
" Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=100 ] = 0.962\n", | |
" Average Precision (AP) @[ IoU=0.75 | area= all | maxDets=100 ] = 0.948\n", | |
" Average Precision (AP) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.034\n", | |
" Average Precision (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.484\n", | |
" Average Precision (AP) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.871\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 1 ] = 0.881\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 10 ] = 0.884\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.884\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.100\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.758\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.906\n", | |
"INFO:tensorflow:Finished evaluation at 2021-05-04-00:37:17\n", | |
"I0504 00:37:17.920741 140215059400576 evaluation.py:275] Finished evaluation at 2021-05-04-00:37:17\n", | |
"INFO:tensorflow:Saving dict for global step 4103: DetectionBoxes_Precision/mAP = 0.8317826, DetectionBoxes_Precision/mAP (large) = 0.87101597, DetectionBoxes_Precision/mAP (medium) = 0.4841115, DetectionBoxes_Precision/mAP (small) = 0.033663366, DetectionBoxes_Precision/[email protected] = 0.96235496, DetectionBoxes_Precision/[email protected] = 0.94771767, DetectionBoxes_Recall/AR@1 = 0.88121945, DetectionBoxes_Recall/AR@10 = 0.88393545, DetectionBoxes_Recall/AR@100 = 0.88393545, DetectionBoxes_Recall/AR@100 (large) = 0.90570116, DetectionBoxes_Recall/AR@100 (medium) = 0.7580175, DetectionBoxes_Recall/AR@100 (small) = 0.1, Loss/classification_loss = 0.16863383, Loss/localization_loss = 0.025558911, Loss/regularization_loss = 0.21923961, Loss/total_loss = 0.4134348, global_step = 4103, learning_rate = 0.039997242, loss = 0.4134348\n", | |
"I0504 00:37:17.921006 140215059400576 estimator.py:2049] Saving dict for global step 4103: DetectionBoxes_Precision/mAP = 0.8317826, DetectionBoxes_Precision/mAP (large) = 0.87101597, DetectionBoxes_Precision/mAP (medium) = 0.4841115, DetectionBoxes_Precision/mAP (small) = 0.033663366, DetectionBoxes_Precision/[email protected] = 0.96235496, DetectionBoxes_Precision/[email protected] = 0.94771767, DetectionBoxes_Recall/AR@1 = 0.88121945, DetectionBoxes_Recall/AR@10 = 0.88393545, DetectionBoxes_Recall/AR@100 = 0.88393545, DetectionBoxes_Recall/AR@100 (large) = 0.90570116, DetectionBoxes_Recall/AR@100 (medium) = 0.7580175, DetectionBoxes_Recall/AR@100 (small) = 0.1, Loss/classification_loss = 0.16863383, Loss/localization_loss = 0.025558911, Loss/regularization_loss = 0.21923961, Loss/total_loss = 0.4134348, global_step = 4103, learning_rate = 0.039997242, loss = 0.4134348\n", | |
"INFO:tensorflow:Saving 'checkpoint_path' summary for global step 4103: /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt-4103\n", | |
"I0504 00:37:17.925047 140215059400576 estimator.py:2109] Saving 'checkpoint_path' summary for global step 4103: /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt-4103\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:global_step/sec: 0.418877\n", | |
"I0504 00:39:31.056219 140215059400576 basic_session_run_hooks.py:692] global_step/sec: 0.418877\n", | |
"INFO:tensorflow:loss = 0.41872814, step = 4200 (238.734 sec)\n", | |
"I0504 00:39:31.057484 140215059400576 basic_session_run_hooks.py:260] loss = 0.41872814, step = 4200 (238.734 sec)\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:global_step/sec: 0.740372\n", | |
"I0504 00:41:46.123488 140215059400576 basic_session_run_hooks.py:692] global_step/sec: 0.740372\n", | |
"INFO:tensorflow:loss = 0.38020697, step = 4300 (135.067 sec)\n", | |
"I0504 00:41:46.124709 140215059400576 basic_session_run_hooks.py:260] loss = 0.38020697, step = 4300 (135.067 sec)\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:global_step/sec: 0.738541\n", | |
"I0504 00:44:01.525508 140215059400576 basic_session_run_hooks.py:692] global_step/sec: 0.738541\n", | |
"INFO:tensorflow:loss = 0.34623438, step = 4400 (135.402 sec)\n", | |
"I0504 00:44:01.526607 140215059400576 basic_session_run_hooks.py:260] loss = 0.34623438, step = 4400 (135.402 sec)\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:Saving checkpoints for 4471 into /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt.\n", | |
"I0504 00:45:36.171920 140215059400576 basic_session_run_hooks.py:606] Saving checkpoints for 4471 into /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt.\n", | |
"INFO:tensorflow:Reading unweighted datasets: ['/content/data/tf_record/pet_faces_train.record-?????-of-00010']\n", | |
"I0504 00:45:38.285873 140215059400576 dataset_builder.py:163] Reading unweighted datasets: ['/content/data/tf_record/pet_faces_train.record-?????-of-00010']\n", | |
"INFO:tensorflow:Reading record datasets for input file: ['/content/data/tf_record/pet_faces_train.record-?????-of-00010']\n", | |
"I0504 00:45:38.286848 140215059400576 dataset_builder.py:80] Reading record datasets for input file: ['/content/data/tf_record/pet_faces_train.record-?????-of-00010']\n", | |
"INFO:tensorflow:Number of filenames to read: 10\n", | |
"I0504 00:45:38.286989 140215059400576 dataset_builder.py:81] Number of filenames to read: 10\n", | |
"WARNING:tensorflow:Entity <bound method TfExampleDecoder.decode of <object_detection.data_decoders.tf_example_decoder.TfExampleDecoder object at 0x7f805d93c6d0>> could not be transformed and will be executed as-is. Please report this to the AutoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: module 'gast' has no attribute 'Num'\n", | |
"W0504 00:45:38.325303 140215059400576 ag_logging.py:146] Entity <bound method TfExampleDecoder.decode of <object_detection.data_decoders.tf_example_decoder.TfExampleDecoder object at 0x7f805d93c6d0>> could not be transformed and will be executed as-is. Please report this to the AutoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: module 'gast' has no attribute 'Num'\n", | |
"WARNING:tensorflow:Entity <function eval_input.<locals>.transform_and_pad_input_data_fn at 0x7f805cd06d40> could not be transformed and will be executed as-is. Please report this to the AutoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: Bad argument number for Name: 3, expecting 4\n", | |
"W0504 00:45:38.486684 140215059400576 ag_logging.py:146] Entity <function eval_input.<locals>.transform_and_pad_input_data_fn at 0x7f805cd06d40> could not be transformed and will be executed as-is. Please report this to the AutoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: Bad argument number for Name: 3, expecting 4\n", | |
"INFO:tensorflow:Calling model_fn.\n", | |
"I0504 00:45:38.964005 140215059400576 estimator.py:1148] Calling model_fn.\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0504 00:45:40.985460 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0504 00:45:41.054814 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0504 00:45:41.123134 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0504 00:45:41.191340 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0504 00:45:41.262410 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0504 00:45:41.331383 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:Done calling model_fn.\n", | |
"I0504 00:45:43.070206 140215059400576 estimator.py:1150] Done calling model_fn.\n", | |
"INFO:tensorflow:Starting evaluation at 2021-05-04T00:45:43Z\n", | |
"I0504 00:45:43.085019 140215059400576 evaluation.py:255] Starting evaluation at 2021-05-04T00:45:43Z\n", | |
"INFO:tensorflow:Graph was finalized.\n", | |
"I0504 00:45:43.531547 140215059400576 monitored_session.py:240] Graph was finalized.\n", | |
"2021-05-04 00:45:43.532221: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-04 00:45:43.532569: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1639] Found device 0 with properties: \n", | |
"name: Tesla P100-PCIE-16GB major: 6 minor: 0 memoryClockRate(GHz): 1.3285\n", | |
"pciBusID: 0000:00:04.0\n", | |
"2021-05-04 00:45:43.532679: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.1\n", | |
"2021-05-04 00:45:43.532699: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcublas.so.10\n", | |
"2021-05-04 00:45:43.532717: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcufft.so.10\n", | |
"2021-05-04 00:45:43.532733: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcurand.so.10\n", | |
"2021-05-04 00:45:43.532747: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusolver.so.10\n", | |
"2021-05-04 00:45:43.532763: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusparse.so.10\n", | |
"2021-05-04 00:45:43.532781: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudnn.so.7\n", | |
"2021-05-04 00:45:43.532852: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-04 00:45:43.533145: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-04 00:45:43.533352: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1767] Adding visible gpu devices: 0\n", | |
"2021-05-04 00:45:43.533390: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1180] Device interconnect StreamExecutor with strength 1 edge matrix:\n", | |
"2021-05-04 00:45:43.533414: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1186] 0 \n", | |
"2021-05-04 00:45:43.533420: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1199] 0: N \n", | |
"2021-05-04 00:45:43.533513: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-04 00:45:43.533737: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-04 00:45:43.533935: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1325] Created TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 15224 MB memory) -> physical GPU (device: 0, name: Tesla P100-PCIE-16GB, pci bus id: 0000:00:04.0, compute capability: 6.0)\n", | |
"INFO:tensorflow:Restoring parameters from /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt-4471\n", | |
"I0504 00:45:43.534970 140215059400576 saver.py:1284] Restoring parameters from /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt-4471\n", | |
"INFO:tensorflow:Running local_init_op.\n", | |
"I0504 00:45:44.609776 140215059400576 session_manager.py:500] Running local_init_op.\n", | |
"INFO:tensorflow:Done running local_init_op.\n", | |
"I0504 00:45:44.770944 140215059400576 session_manager.py:502] Done running local_init_op.\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:Performing evaluation on 2568 images.\n", | |
"I0504 00:46:51.560856 140210424129280 coco_evaluation.py:293] Performing evaluation on 2568 images.\n", | |
"creating index...\n", | |
"index created!\n", | |
"INFO:tensorflow:Loading and preparing annotation results...\n", | |
"I0504 00:46:51.567034 140210424129280 coco_tools.py:116] Loading and preparing annotation results...\n", | |
"INFO:tensorflow:DONE (t=0.20s)\n", | |
"I0504 00:46:51.771404 140210424129280 coco_tools.py:138] DONE (t=0.20s)\n", | |
"creating index...\n", | |
"index created!\n", | |
"Running per image evaluation...\n", | |
"Evaluate annotation type *bbox*\n", | |
"DONE (t=19.81s).\n", | |
"Accumulating evaluation results...\n", | |
"DONE (t=6.68s).\n", | |
" Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.857\n", | |
" Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=100 ] = 0.980\n", | |
" Average Precision (AP) @[ IoU=0.75 | area= all | maxDets=100 ] = 0.969\n", | |
" Average Precision (AP) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.017\n", | |
" Average Precision (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.599\n", | |
" Average Precision (AP) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.886\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 1 ] = 0.892\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 10 ] = 0.894\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.894\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.200\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.792\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.912\n", | |
"INFO:tensorflow:Finished evaluation at 2021-05-04-00:47:19\n", | |
"I0504 00:47:19.259883 140215059400576 evaluation.py:275] Finished evaluation at 2021-05-04-00:47:19\n", | |
"INFO:tensorflow:Saving dict for global step 4471: DetectionBoxes_Precision/mAP = 0.85685056, DetectionBoxes_Precision/mAP (large) = 0.8855686, DetectionBoxes_Precision/mAP (medium) = 0.5988093, DetectionBoxes_Precision/mAP (small) = 0.016831683, DetectionBoxes_Precision/[email protected] = 0.9797586, DetectionBoxes_Precision/[email protected] = 0.96924186, DetectionBoxes_Recall/AR@1 = 0.8917457, DetectionBoxes_Recall/AR@10 = 0.8937107, DetectionBoxes_Recall/AR@100 = 0.8937107, DetectionBoxes_Recall/AR@100 (large) = 0.91176426, DetectionBoxes_Recall/AR@100 (medium) = 0.7915283, DetectionBoxes_Recall/AR@100 (small) = 0.2, Loss/classification_loss = 0.13611425, Loss/localization_loss = 0.02159942, Loss/regularization_loss = 0.21761687, Loss/total_loss = 0.37532955, global_step = 4471, learning_rate = 0.039996196, loss = 0.37532955\n", | |
"I0504 00:47:19.260176 140215059400576 estimator.py:2049] Saving dict for global step 4471: DetectionBoxes_Precision/mAP = 0.85685056, DetectionBoxes_Precision/mAP (large) = 0.8855686, DetectionBoxes_Precision/mAP (medium) = 0.5988093, DetectionBoxes_Precision/mAP (small) = 0.016831683, DetectionBoxes_Precision/[email protected] = 0.9797586, DetectionBoxes_Precision/[email protected] = 0.96924186, DetectionBoxes_Recall/AR@1 = 0.8917457, DetectionBoxes_Recall/AR@10 = 0.8937107, DetectionBoxes_Recall/AR@100 = 0.8937107, DetectionBoxes_Recall/AR@100 (large) = 0.91176426, DetectionBoxes_Recall/AR@100 (medium) = 0.7915283, DetectionBoxes_Recall/AR@100 (small) = 0.2, Loss/classification_loss = 0.13611425, Loss/localization_loss = 0.02159942, Loss/regularization_loss = 0.21761687, Loss/total_loss = 0.37532955, global_step = 4471, learning_rate = 0.039996196, loss = 0.37532955\n", | |
"INFO:tensorflow:Saving 'checkpoint_path' summary for global step 4471: /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt-4471\n", | |
"I0504 00:47:19.263885 140215059400576 estimator.py:2109] Saving 'checkpoint_path' summary for global step 4471: /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt-4471\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:global_step/sec: 0.419648\n", | |
"I0504 00:47:59.820444 140215059400576 basic_session_run_hooks.py:692] global_step/sec: 0.419648\n", | |
"INFO:tensorflow:loss = 0.32304123, step = 4500 (238.295 sec)\n", | |
"I0504 00:47:59.821493 140215059400576 basic_session_run_hooks.py:260] loss = 0.32304123, step = 4500 (238.295 sec)\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:global_step/sec: 0.738003\n", | |
"I0504 00:50:15.321199 140215059400576 basic_session_run_hooks.py:692] global_step/sec: 0.738003\n", | |
"INFO:tensorflow:loss = 0.38807705, step = 4600 (135.501 sec)\n", | |
"I0504 00:50:15.322352 140215059400576 basic_session_run_hooks.py:260] loss = 0.38807705, step = 4600 (135.501 sec)\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:global_step/sec: 0.739737\n", | |
"I0504 00:52:30.504318 140215059400576 basic_session_run_hooks.py:692] global_step/sec: 0.739737\n", | |
"INFO:tensorflow:loss = 0.33238897, step = 4700 (135.183 sec)\n", | |
"I0504 00:52:30.505529 140215059400576 basic_session_run_hooks.py:260] loss = 0.33238897, step = 4700 (135.183 sec)\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:global_step/sec: 0.735586\n", | |
"I0504 00:54:46.450327 140215059400576 basic_session_run_hooks.py:692] global_step/sec: 0.735586\n", | |
"INFO:tensorflow:loss = 0.39697814, step = 4800 (135.946 sec)\n", | |
"I0504 00:54:46.451421 140215059400576 basic_session_run_hooks.py:260] loss = 0.39697814, step = 4800 (135.946 sec)\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:Saving checkpoints for 4838 into /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt.\n", | |
"I0504 00:55:36.339880 140215059400576 basic_session_run_hooks.py:606] Saving checkpoints for 4838 into /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt.\n", | |
"INFO:tensorflow:Reading unweighted datasets: ['/content/data/tf_record/pet_faces_train.record-?????-of-00010']\n", | |
"I0504 00:55:38.447259 140215059400576 dataset_builder.py:163] Reading unweighted datasets: ['/content/data/tf_record/pet_faces_train.record-?????-of-00010']\n", | |
"INFO:tensorflow:Reading record datasets for input file: ['/content/data/tf_record/pet_faces_train.record-?????-of-00010']\n", | |
"I0504 00:55:38.448234 140215059400576 dataset_builder.py:80] Reading record datasets for input file: ['/content/data/tf_record/pet_faces_train.record-?????-of-00010']\n", | |
"INFO:tensorflow:Number of filenames to read: 10\n", | |
"I0504 00:55:38.448386 140215059400576 dataset_builder.py:81] Number of filenames to read: 10\n", | |
"WARNING:tensorflow:Entity <bound method TfExampleDecoder.decode of <object_detection.data_decoders.tf_example_decoder.TfExampleDecoder object at 0x7f8060f32f50>> could not be transformed and will be executed as-is. Please report this to the AutoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: module 'gast' has no attribute 'Num'\n", | |
"W0504 00:55:38.485988 140215059400576 ag_logging.py:146] Entity <bound method TfExampleDecoder.decode of <object_detection.data_decoders.tf_example_decoder.TfExampleDecoder object at 0x7f8060f32f50>> could not be transformed and will be executed as-is. Please report this to the AutoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: module 'gast' has no attribute 'Num'\n", | |
"WARNING:tensorflow:Entity <function eval_input.<locals>.transform_and_pad_input_data_fn at 0x7f805d37d830> could not be transformed and will be executed as-is. Please report this to the AutoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: Bad argument number for Name: 3, expecting 4\n", | |
"W0504 00:55:38.655124 140215059400576 ag_logging.py:146] Entity <function eval_input.<locals>.transform_and_pad_input_data_fn at 0x7f805d37d830> could not be transformed and will be executed as-is. Please report this to the AutoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: Bad argument number for Name: 3, expecting 4\n", | |
"INFO:tensorflow:Calling model_fn.\n", | |
"I0504 00:55:39.201994 140215059400576 estimator.py:1148] Calling model_fn.\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0504 00:55:41.364953 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0504 00:55:41.434043 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0504 00:55:41.502981 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0504 00:55:41.571713 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0504 00:55:41.639482 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0504 00:55:41.711484 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:Done calling model_fn.\n", | |
"I0504 00:55:43.432304 140215059400576 estimator.py:1150] Done calling model_fn.\n", | |
"INFO:tensorflow:Starting evaluation at 2021-05-04T00:55:43Z\n", | |
"I0504 00:55:43.447510 140215059400576 evaluation.py:255] Starting evaluation at 2021-05-04T00:55:43Z\n", | |
"INFO:tensorflow:Graph was finalized.\n", | |
"I0504 00:55:43.906028 140215059400576 monitored_session.py:240] Graph was finalized.\n", | |
"2021-05-04 00:55:43.906670: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-04 00:55:43.906977: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1639] Found device 0 with properties: \n", | |
"name: Tesla P100-PCIE-16GB major: 6 minor: 0 memoryClockRate(GHz): 1.3285\n", | |
"pciBusID: 0000:00:04.0\n", | |
"2021-05-04 00:55:43.907096: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.1\n", | |
"2021-05-04 00:55:43.907132: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcublas.so.10\n", | |
"2021-05-04 00:55:43.907159: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcufft.so.10\n", | |
"2021-05-04 00:55:43.907183: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcurand.so.10\n", | |
"2021-05-04 00:55:43.907205: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusolver.so.10\n", | |
"2021-05-04 00:55:43.907226: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusparse.so.10\n", | |
"2021-05-04 00:55:43.907248: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudnn.so.7\n", | |
"2021-05-04 00:55:43.907335: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-04 00:55:43.907614: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-04 00:55:43.907829: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1767] Adding visible gpu devices: 0\n", | |
"2021-05-04 00:55:43.907917: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1180] Device interconnect StreamExecutor with strength 1 edge matrix:\n", | |
"2021-05-04 00:55:43.907932: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1186] 0 \n", | |
"2021-05-04 00:55:43.907942: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1199] 0: N \n", | |
"2021-05-04 00:55:43.908043: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-04 00:55:43.908316: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-04 00:55:43.908537: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1325] Created TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 15224 MB memory) -> physical GPU (device: 0, name: Tesla P100-PCIE-16GB, pci bus id: 0000:00:04.0, compute capability: 6.0)\n", | |
"INFO:tensorflow:Restoring parameters from /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt-4838\n", | |
"I0504 00:55:43.909493 140215059400576 saver.py:1284] Restoring parameters from /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt-4838\n", | |
"INFO:tensorflow:Running local_init_op.\n", | |
"I0504 00:55:44.964654 140215059400576 session_manager.py:500] Running local_init_op.\n", | |
"INFO:tensorflow:Done running local_init_op.\n", | |
"I0504 00:55:45.104933 140215059400576 session_manager.py:502] Done running local_init_op.\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:Performing evaluation on 2568 images.\n", | |
"I0504 00:56:52.510566 140210415736576 coco_evaluation.py:293] Performing evaluation on 2568 images.\n", | |
"creating index...\n", | |
"index created!\n", | |
"INFO:tensorflow:Loading and preparing annotation results...\n", | |
"I0504 00:56:52.518196 140210415736576 coco_tools.py:116] Loading and preparing annotation results...\n", | |
"INFO:tensorflow:DONE (t=0.19s)\n", | |
"I0504 00:56:52.708378 140210415736576 coco_tools.py:138] DONE (t=0.19s)\n", | |
"creating index...\n", | |
"index created!\n", | |
"Running per image evaluation...\n", | |
"Evaluate annotation type *bbox*\n", | |
"DONE (t=19.40s).\n", | |
"Accumulating evaluation results...\n", | |
"DONE (t=6.77s).\n", | |
" Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.842\n", | |
" Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=100 ] = 0.971\n", | |
" Average Precision (AP) @[ IoU=0.75 | area= all | maxDets=100 ] = 0.954\n", | |
" Average Precision (AP) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.252\n", | |
" Average Precision (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.578\n", | |
" Average Precision (AP) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.874\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 1 ] = 0.881\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 10 ] = 0.885\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.885\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.250\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.765\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.905\n", | |
"INFO:tensorflow:Finished evaluation at 2021-05-04-00:57:19\n", | |
"I0504 00:57:19.849217 140215059400576 evaluation.py:275] Finished evaluation at 2021-05-04-00:57:19\n", | |
"INFO:tensorflow:Saving dict for global step 4838: DetectionBoxes_Precision/mAP = 0.8417523, DetectionBoxes_Precision/mAP (large) = 0.87389356, DetectionBoxes_Precision/mAP (medium) = 0.5784151, DetectionBoxes_Precision/mAP (small) = 0.25247526, DetectionBoxes_Precision/[email protected] = 0.9710778, DetectionBoxes_Precision/[email protected] = 0.9540878, DetectionBoxes_Recall/AR@1 = 0.88101786, DetectionBoxes_Recall/AR@10 = 0.88519293, DetectionBoxes_Recall/AR@100 = 0.88519293, DetectionBoxes_Recall/AR@100 (large) = 0.9049079, DetectionBoxes_Recall/AR@100 (medium) = 0.7649073, DetectionBoxes_Recall/AR@100 (small) = 0.25, Loss/classification_loss = 0.1509938, Loss/localization_loss = 0.025918532, Loss/regularization_loss = 0.21599802, Loss/total_loss = 0.39290625, global_step = 4838, learning_rate = 0.03999498, loss = 0.39290625\n", | |
"I0504 00:57:19.849468 140215059400576 estimator.py:2049] Saving dict for global step 4838: DetectionBoxes_Precision/mAP = 0.8417523, DetectionBoxes_Precision/mAP (large) = 0.87389356, DetectionBoxes_Precision/mAP (medium) = 0.5784151, DetectionBoxes_Precision/mAP (small) = 0.25247526, DetectionBoxes_Precision/[email protected] = 0.9710778, DetectionBoxes_Precision/[email protected] = 0.9540878, DetectionBoxes_Recall/AR@1 = 0.88101786, DetectionBoxes_Recall/AR@10 = 0.88519293, DetectionBoxes_Recall/AR@100 = 0.88519293, DetectionBoxes_Recall/AR@100 (large) = 0.9049079, DetectionBoxes_Recall/AR@100 (medium) = 0.7649073, DetectionBoxes_Recall/AR@100 (small) = 0.25, Loss/classification_loss = 0.1509938, Loss/localization_loss = 0.025918532, Loss/regularization_loss = 0.21599802, Loss/total_loss = 0.39290625, global_step = 4838, learning_rate = 0.03999498, loss = 0.39290625\n", | |
"INFO:tensorflow:Saving 'checkpoint_path' summary for global step 4838: /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt-4838\n", | |
"I0504 00:57:19.853150 140215059400576 estimator.py:2109] Saving 'checkpoint_path' summary for global step 4838: /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt-4838\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:global_step/sec: 0.41944\n", | |
"I0504 00:58:44.863583 140215059400576 basic_session_run_hooks.py:692] global_step/sec: 0.41944\n", | |
"INFO:tensorflow:loss = 0.35798532, step = 4900 (238.413 sec)\n", | |
"I0504 00:58:44.864837 140215059400576 basic_session_run_hooks.py:260] loss = 0.35798532, step = 4900 (238.413 sec)\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:Saving checkpoints for 5000 into /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt.\n", | |
"I0504 01:00:58.754750 140215059400576 basic_session_run_hooks.py:606] Saving checkpoints for 5000 into /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt.\n", | |
"INFO:tensorflow:Skip the current checkpoint eval due to throttle secs (600 secs).\n", | |
"I0504 01:01:00.836331 140215059400576 training.py:527] Skip the current checkpoint eval due to throttle secs (600 secs).\n", | |
"INFO:tensorflow:Reading unweighted datasets: ['/content/data/tf_record/pet_faces_train.record-?????-of-00010']\n", | |
"I0504 01:01:00.861598 140215059400576 dataset_builder.py:163] Reading unweighted datasets: ['/content/data/tf_record/pet_faces_train.record-?????-of-00010']\n", | |
"INFO:tensorflow:Reading record datasets for input file: ['/content/data/tf_record/pet_faces_train.record-?????-of-00010']\n", | |
"I0504 01:01:00.862543 140215059400576 dataset_builder.py:80] Reading record datasets for input file: ['/content/data/tf_record/pet_faces_train.record-?????-of-00010']\n", | |
"INFO:tensorflow:Number of filenames to read: 10\n", | |
"I0504 01:01:00.862663 140215059400576 dataset_builder.py:81] Number of filenames to read: 10\n", | |
"WARNING:tensorflow:Entity <bound method TfExampleDecoder.decode of <object_detection.data_decoders.tf_example_decoder.TfExampleDecoder object at 0x7f806346ff10>> could not be transformed and will be executed as-is. Please report this to the AutoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: module 'gast' has no attribute 'Num'\n", | |
"W0504 01:01:00.899821 140215059400576 ag_logging.py:146] Entity <bound method TfExampleDecoder.decode of <object_detection.data_decoders.tf_example_decoder.TfExampleDecoder object at 0x7f806346ff10>> could not be transformed and will be executed as-is. Please report this to the AutoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: module 'gast' has no attribute 'Num'\n", | |
"WARNING:tensorflow:Entity <function eval_input.<locals>.transform_and_pad_input_data_fn at 0x7f852836dcb0> could not be transformed and will be executed as-is. Please report this to the AutoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: Bad argument number for Name: 3, expecting 4\n", | |
"W0504 01:01:01.059637 140215059400576 ag_logging.py:146] Entity <function eval_input.<locals>.transform_and_pad_input_data_fn at 0x7f852836dcb0> could not be transformed and will be executed as-is. Please report this to the AutoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: Bad argument number for Name: 3, expecting 4\n", | |
"INFO:tensorflow:Calling model_fn.\n", | |
"I0504 01:01:01.534383 140215059400576 estimator.py:1148] Calling model_fn.\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0504 01:01:03.650071 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0504 01:01:03.720171 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0504 01:01:03.787953 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0504 01:01:03.856566 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0504 01:01:03.924641 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0504 01:01:03.994073 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:Done calling model_fn.\n", | |
"I0504 01:01:06.120358 140215059400576 estimator.py:1150] Done calling model_fn.\n", | |
"INFO:tensorflow:Starting evaluation at 2021-05-04T01:01:06Z\n", | |
"I0504 01:01:06.135504 140215059400576 evaluation.py:255] Starting evaluation at 2021-05-04T01:01:06Z\n", | |
"INFO:tensorflow:Graph was finalized.\n", | |
"I0504 01:01:06.595707 140215059400576 monitored_session.py:240] Graph was finalized.\n", | |
"2021-05-04 01:01:06.596335: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-04 01:01:06.596643: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1639] Found device 0 with properties: \n", | |
"name: Tesla P100-PCIE-16GB major: 6 minor: 0 memoryClockRate(GHz): 1.3285\n", | |
"pciBusID: 0000:00:04.0\n", | |
"2021-05-04 01:01:06.596733: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.1\n", | |
"2021-05-04 01:01:06.596753: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcublas.so.10\n", | |
"2021-05-04 01:01:06.596766: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcufft.so.10\n", | |
"2021-05-04 01:01:06.596781: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcurand.so.10\n", | |
"2021-05-04 01:01:06.596795: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusolver.so.10\n", | |
"2021-05-04 01:01:06.596810: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusparse.so.10\n", | |
"2021-05-04 01:01:06.596825: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudnn.so.7\n", | |
"2021-05-04 01:01:06.596890: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-04 01:01:06.597178: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-04 01:01:06.597396: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1767] Adding visible gpu devices: 0\n", | |
"2021-05-04 01:01:06.597438: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1180] Device interconnect StreamExecutor with strength 1 edge matrix:\n", | |
"2021-05-04 01:01:06.597449: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1186] 0 \n", | |
"2021-05-04 01:01:06.597456: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1199] 0: N \n", | |
"2021-05-04 01:01:06.597543: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-04 01:01:06.597787: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-04 01:01:06.597999: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1325] Created TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 15224 MB memory) -> physical GPU (device: 0, name: Tesla P100-PCIE-16GB, pci bus id: 0000:00:04.0, compute capability: 6.0)\n", | |
"INFO:tensorflow:Restoring parameters from /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt-5000\n", | |
"I0504 01:01:06.601448 140215059400576 saver.py:1284] Restoring parameters from /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt-5000\n", | |
"INFO:tensorflow:Running local_init_op.\n", | |
"I0504 01:01:07.712863 140215059400576 session_manager.py:500] Running local_init_op.\n", | |
"INFO:tensorflow:Done running local_init_op.\n", | |
"I0504 01:01:07.899731 140215059400576 session_manager.py:502] Done running local_init_op.\n", | |
"Corrupt JPEG data: premature end of data segment\n", | |
"INFO:tensorflow:Performing evaluation on 2568 images.\n", | |
"I0504 01:02:14.895640 140210432521984 coco_evaluation.py:293] Performing evaluation on 2568 images.\n", | |
"creating index...\n", | |
"index created!\n", | |
"INFO:tensorflow:Loading and preparing annotation results...\n", | |
"I0504 01:02:14.906803 140210432521984 coco_tools.py:116] Loading and preparing annotation results...\n", | |
"INFO:tensorflow:DONE (t=0.21s)\n", | |
"I0504 01:02:15.115651 140210432521984 coco_tools.py:138] DONE (t=0.21s)\n", | |
"creating index...\n", | |
"index created!\n", | |
"Running per image evaluation...\n", | |
"Evaluate annotation type *bbox*\n", | |
"DONE (t=19.63s).\n", | |
"Accumulating evaluation results...\n", | |
"DONE (t=6.65s).\n", | |
" Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.844\n", | |
" Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=100 ] = 0.965\n", | |
" Average Precision (AP) @[ IoU=0.75 | area= all | maxDets=100 ] = 0.957\n", | |
" Average Precision (AP) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.217\n", | |
" Average Precision (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.594\n", | |
" Average Precision (AP) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.877\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 1 ] = 0.891\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 10 ] = 0.893\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.893\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.250\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.787\n", | |
" Average Recall (AR) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.913\n", | |
"INFO:tensorflow:Finished evaluation at 2021-05-04-01:02:42\n", | |
"I0504 01:02:42.384744 140215059400576 evaluation.py:275] Finished evaluation at 2021-05-04-01:02:42\n", | |
"INFO:tensorflow:Saving dict for global step 5000: DetectionBoxes_Precision/mAP = 0.84409505, DetectionBoxes_Precision/mAP (large) = 0.87721324, DetectionBoxes_Precision/mAP (medium) = 0.59417856, DetectionBoxes_Precision/mAP (small) = 0.21749175, DetectionBoxes_Precision/[email protected] = 0.9647228, DetectionBoxes_Precision/[email protected] = 0.95705926, DetectionBoxes_Recall/AR@1 = 0.8907396, DetectionBoxes_Recall/AR@10 = 0.89296573, DetectionBoxes_Recall/AR@100 = 0.89296573, DetectionBoxes_Recall/AR@100 (large) = 0.91329783, DetectionBoxes_Recall/AR@100 (medium) = 0.7871724, DetectionBoxes_Recall/AR@100 (small) = 0.25, Loss/classification_loss = 0.14837511, Loss/localization_loss = 0.02272812, Loss/regularization_loss = 0.2152484, Loss/total_loss = 0.3863538, global_step = 5000, learning_rate = 0.039994393, loss = 0.3863538\n", | |
"I0504 01:02:42.385046 140215059400576 estimator.py:2049] Saving dict for global step 5000: DetectionBoxes_Precision/mAP = 0.84409505, DetectionBoxes_Precision/mAP (large) = 0.87721324, DetectionBoxes_Precision/mAP (medium) = 0.59417856, DetectionBoxes_Precision/mAP (small) = 0.21749175, DetectionBoxes_Precision/[email protected] = 0.9647228, DetectionBoxes_Precision/[email protected] = 0.95705926, DetectionBoxes_Recall/AR@1 = 0.8907396, DetectionBoxes_Recall/AR@10 = 0.89296573, DetectionBoxes_Recall/AR@100 = 0.89296573, DetectionBoxes_Recall/AR@100 (large) = 0.91329783, DetectionBoxes_Recall/AR@100 (medium) = 0.7871724, DetectionBoxes_Recall/AR@100 (small) = 0.25, Loss/classification_loss = 0.14837511, Loss/localization_loss = 0.02272812, Loss/regularization_loss = 0.2152484, Loss/total_loss = 0.3863538, global_step = 5000, learning_rate = 0.039994393, loss = 0.3863538\n", | |
"INFO:tensorflow:Saving 'checkpoint_path' summary for global step 5000: /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt-5000\n", | |
"I0504 01:02:42.388940 140215059400576 estimator.py:2109] Saving 'checkpoint_path' summary for global step 5000: /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt-5000\n", | |
"INFO:tensorflow:Performing the final export in the end of training.\n", | |
"I0504 01:02:42.389613 140215059400576 exporter.py:410] Performing the final export in the end of training.\n", | |
"INFO:tensorflow:Calling model_fn.\n", | |
"I0504 01:02:42.629983 140215059400576 estimator.py:1148] Calling model_fn.\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0504 01:02:44.649070 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0504 01:02:44.721361 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0504 01:02:44.788778 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0504 01:02:44.856194 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0504 01:02:44.928780 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:depth of additional conv before box predictor: 0\n", | |
"I0504 01:02:45.000893 140215059400576 convolutional_box_predictor.py:156] depth of additional conv before box predictor: 0\n", | |
"INFO:tensorflow:Done calling model_fn.\n", | |
"I0504 01:02:46.088902 140215059400576 estimator.py:1150] Done calling model_fn.\n", | |
"WARNING:tensorflow:From /tensorflow-1.15.2/python3.7/tensorflow_core/python/saved_model/signature_def_utils_impl.py:201: build_tensor_info (from tensorflow.python.saved_model.utils_impl) is deprecated and will be removed in a future version.\n", | |
"Instructions for updating:\n", | |
"This function will only be available through the v1 compatibility library as tf.compat.v1.saved_model.utils.build_tensor_info or tf.compat.v1.saved_model.build_tensor_info.\n", | |
"W0504 01:02:46.089168 140215059400576 deprecation.py:323] From /tensorflow-1.15.2/python3.7/tensorflow_core/python/saved_model/signature_def_utils_impl.py:201: build_tensor_info (from tensorflow.python.saved_model.utils_impl) is deprecated and will be removed in a future version.\n", | |
"Instructions for updating:\n", | |
"This function will only be available through the v1 compatibility library as tf.compat.v1.saved_model.utils.build_tensor_info or tf.compat.v1.saved_model.build_tensor_info.\n", | |
"INFO:tensorflow:Signatures INCLUDED in export for Classify: None\n", | |
"I0504 01:02:46.089719 140215059400576 export_utils.py:170] Signatures INCLUDED in export for Classify: None\n", | |
"INFO:tensorflow:Signatures INCLUDED in export for Regress: None\n", | |
"I0504 01:02:46.089831 140215059400576 export_utils.py:170] Signatures INCLUDED in export for Regress: None\n", | |
"INFO:tensorflow:Signatures INCLUDED in export for Predict: ['tensorflow/serving/predict', 'serving_default']\n", | |
"I0504 01:02:46.089886 140215059400576 export_utils.py:170] Signatures INCLUDED in export for Predict: ['tensorflow/serving/predict', 'serving_default']\n", | |
"INFO:tensorflow:Signatures INCLUDED in export for Train: None\n", | |
"I0504 01:02:46.089935 140215059400576 export_utils.py:170] Signatures INCLUDED in export for Train: None\n", | |
"INFO:tensorflow:Signatures INCLUDED in export for Eval: None\n", | |
"I0504 01:02:46.089978 140215059400576 export_utils.py:170] Signatures INCLUDED in export for Eval: None\n", | |
"2021-05-04 01:02:46.090425: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-04 01:02:46.090723: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1639] Found device 0 with properties: \n", | |
"name: Tesla P100-PCIE-16GB major: 6 minor: 0 memoryClockRate(GHz): 1.3285\n", | |
"pciBusID: 0000:00:04.0\n", | |
"2021-05-04 01:02:46.090814: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.1\n", | |
"2021-05-04 01:02:46.090834: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcublas.so.10\n", | |
"2021-05-04 01:02:46.090849: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcufft.so.10\n", | |
"2021-05-04 01:02:46.090864: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcurand.so.10\n", | |
"2021-05-04 01:02:46.090878: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusolver.so.10\n", | |
"2021-05-04 01:02:46.090892: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusparse.so.10\n", | |
"2021-05-04 01:02:46.090906: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudnn.so.7\n", | |
"2021-05-04 01:02:46.090973: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-04 01:02:46.091218: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-04 01:02:46.091406: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1767] Adding visible gpu devices: 0\n", | |
"2021-05-04 01:02:46.091445: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1180] Device interconnect StreamExecutor with strength 1 edge matrix:\n", | |
"2021-05-04 01:02:46.091475: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1186] 0 \n", | |
"2021-05-04 01:02:46.091481: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1199] 0: N \n", | |
"2021-05-04 01:02:46.091562: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-04 01:02:46.091784: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:983] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\n", | |
"2021-05-04 01:02:46.091988: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1325] Created TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 15224 MB memory) -> physical GPU (device: 0, name: Tesla P100-PCIE-16GB, pci bus id: 0000:00:04.0, compute capability: 6.0)\n", | |
"INFO:tensorflow:Restoring parameters from /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt-5000\n", | |
"I0504 01:02:46.094195 140215059400576 saver.py:1284] Restoring parameters from /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/model.ckpt-5000\n", | |
"INFO:tensorflow:Assets added to graph.\n", | |
"I0504 01:02:46.613681 140215059400576 builder_impl.py:665] Assets added to graph.\n", | |
"INFO:tensorflow:No assets to write.\n", | |
"I0504 01:02:46.613869 140215059400576 builder_impl.py:460] No assets to write.\n", | |
"INFO:tensorflow:SavedModel written to: /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/export/Servo/temp-b'1620090162'/saved_model.pb\n", | |
"I0504 01:02:47.470509 140215059400576 builder_impl.py:425] SavedModel written to: /content/data/train_ssdlite_mobilenet_v3_large_320x320_pet/export/Servo/temp-b'1620090162'/saved_model.pb\n", | |
"INFO:tensorflow:Loss for final step: 0.3643322.\n", | |
"I0504 01:02:47.987567 140215059400576 estimator.py:371] Loss for final step: 0.3643322.\n" | |
], | |
"name": "stdout" | |
} | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"metadata": { | |
"colab": { | |
"resources": { | |
"http://localhost:6006/": { | |
"data": "PCFkb2N0eXBlIGh0bWw+PCEtLQpAbGljZW5zZQpDb3B5cmlnaHQgMjAxNiBUaGUgVGVuc29yRmxvdyBBdXRob3JzLiBBbGwgUmlnaHRzIFJlc2VydmVkLgoKTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlICJMaWNlbnNlIik7CnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS4KWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0CgogICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wCgpVbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlCmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuICJBUyBJUyIgQkFTSVMsCldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLgpTZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kCmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLgotLT48bWV0YSBjaGFyc2V0PSJ1dGYtOCI+Cjx0aXRsZT5UZW5zb3JCb2FyZDwvdGl0bGU+CjxsaW5rIHJlbD0ic2hvcnRjdXQgaWNvbiIgaHJlZj0iZGF0YTppbWFnZS9wbmc7YmFzZTY0LGlWQk9SdzBLR2dvQUFBQU5TVWhFVWdBQUFNUUFBQURFQ0FNQUFBRDNlSDVaQUFBQUJHZEJUVUVBQUxHUEMveGhCUUFBQUFGelVrZENBSzdPSE9rQUFBRC9VRXhVUmZGbEtmYUVMdkZtS2ZOeUsvNjdOdldBTGY2OE52NjlOdk54Sy8yME5meXlOUDIyTmZOMEsvSnJLdnFoTXYyek5mMjVOZjI0TmYyM05mZU9ML3l6TlB5dk5QSm9LdmlXTVBtZU1mTjFLL1dCTGZlUEwvRm5LZmVNTC9xbE12UjdMUG1jTWZlTEwvYUpMdlI1TFBGb0tmSnVLdlIzTFA2Nk52eXdOUGVOTC9WL0xmYUlMdjIxTmYyNk5mTnpLL052Sy9SNkxQbWFNZnl4TlBxZk12VitMZnVyTS9pU01QbWJNZkp2S3ZtZE1mdW1NL3FpTXZtWk1meXROUEpxS3Z5c05QTjJLL2lZTVBOd0svdXBNL0p0S3ZKc0t2aVZNUGFITHZhR0x2SnBLdlI4TFBhS0x2cWtNdnVxTS9hRkx2UjRMUHVvTS9pVE1QV0RMZmlSTVBtWU1YUzBuZ2tBQUFMb1NVUkJWSGphN2RybmN0cEFGSWJoRlVJU1NLSjNNS1lhMCt5NHhUVzk5MzcvMTVKa0pobFRqaHJTckhSbXZ1Zi9hczZMMFlMRkNnRUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQU1CSjZuamVucXNwemduUHJzckdYOVpwaTJ0Q3JtbmM2K2RZTnRoVlk1V3BNbXhRTFdQZE1zT3VZVnd6TmozZWkydDNtUXdhVjQzQkpQRENTMk5iSjVhRWV1WC8rOXFjalFPdGZGSWtJa3J2WTJnNE1WY21PQnNGV2Jvd0tPL2tOeWo2MmdScEpjRGFQQmx4THIxQjB6ZEcwQy84THpiSmlKcnNodXZ5MWd6bEE5K3JEOG1Ja3V5SUpqRkUzL2Rxbll3b1NtN0lVRVBvRC93dXQ4aUlndVNJRGpsRnhlL3lmWEw1dnVTSTIxQlRaTExoWG9PSUxNTzhIeHdhL0w4YkkwTGZtVWRoR293YjJadlQwZTU3cEZORGdCMDZJbFZ5am1tSUJsMlQvbmw5Unc2U0Q5R2dTRy9RMHVRa2FXM1hobW92S1EzZUZRNE4yVW85T1ExZUZac05lcmY3dlArck80cmhtWTFMZzN2RlZvUDgrOEJYZzFzRm53Ym5DazROVGhXOEd1aUtCRGRrVlZ0VE5Gdk5lbFZzTnFUYnlXbklPTTJvZVRSb3lXdndtcEpIZy91Y1hCcmNKdVhUNER3cnB3WmkydnkwVkN4OFl0WGcvRDJiVTRPZml1UTNlRmZFMktENGJmQ3FpTE5COTkzZ1hzR2x3YTJDVDROekJhY0dJVlE2WXNpcFFkaDB4RWRPRFVLakl4clNwODhvblo4emJiRkxnMURvaUZPNUJYdkRHdjJNeTkvSmhVVDhKVVpUSTB5RGFOSExCeklidnFURE5ZaFVpVncva2RqUTFrTTJDSEZEUGpLVytLenlSVEYwZy9nYTl3OXkrZkFOUXB4dlg4Q1UrTnk3RlVXRGVGM1krZzNsUk9JZjRrMFVEWDllQ3l2TzUzMVB5WWhIZ2E5enZQWkpVNWI3M1kvZVhqOEh2OUQ0OG42SGFGNUxiY2pSdDhUWlR0ZGE1TTFEZlhuYmtYMUMwU0hDRkt6UUI1RmU4b3A0R05HTkhhdnZaRVNiVndUNXI2VzF4eXVDUEJZM1k5WWdEcXprbkgvZTNZZk56enVMMzBsMEllYnJaNWtLdHVESVh0MW44NjhFVDZrZjMvNDl0THZyQ2NaeUY4UHUyMTVkQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFjUEliTnJCaE9hQlh1Y29BQUFBQVNVVk9SSzVDWUlJPSI+CjxsaW5rIHJlbD0iYXBwbGUtdG91Y2gtaWNvbiIgaHJlZj0iZGF0YTppbWFnZS9wbmc7YmFzZTY0LGlWQk9SdzBLR2dvQUFBQU5TVWhFVWdBQUFNUUFBQURFQ0FNQUFBRDNlSDVaQUFBQUJHZEJUVUVBQUxHUEMveGhCUUFBQUFGelVrZENBSzdPSE9rQUFBRC9VRXhVUmZGbEtmYUVMdkZtS2ZOeUsvNjdOdldBTGY2OE52NjlOdk54Sy8yME5meXlOUDIyTmZOMEsvSnJLdnFoTXYyek5mMjVOZjI0TmYyM05mZU9ML3l6TlB5dk5QSm9LdmlXTVBtZU1mTjFLL1dCTGZlUEwvRm5LZmVNTC9xbE12UjdMUG1jTWZlTEwvYUpMdlI1TFBGb0tmSnVLdlIzTFA2Nk52eXdOUGVOTC9WL0xmYUlMdjIxTmYyNk5mTnpLL052Sy9SNkxQbWFNZnl4TlBxZk12VitMZnVyTS9pU01QbWJNZkp2S3ZtZE1mdW1NL3FpTXZtWk1meXROUEpxS3Z5c05QTjJLL2lZTVBOd0svdXBNL0p0S3ZKc0t2aVZNUGFITHZhR0x2SnBLdlI4TFBhS0x2cWtNdnVxTS9hRkx2UjRMUHVvTS9pVE1QV0RMZmlSTVBtWU1YUzBuZ2tBQUFMb1NVUkJWSGphN2RybmN0cEFGSWJoRlVJU1NLSjNNS1lhMCt5NHhUVzk5MzcvMTVKa0pobFRqaHJTckhSbXZ1Zi9hczZMMFlMRkNnRUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQU1CSjZuamVucXNwemduUHJzckdYOVpwaTJ0Q3JtbmM2K2RZTnRoVlk1V3BNbXhRTFdQZE1zT3VZVnd6TmozZWkydDNtUXdhVjQzQkpQRENTMk5iSjVhRWV1WC8rOXFjalFPdGZGSWtJa3J2WTJnNE1WY21PQnNGV2Jvd0tPL2tOeWo2MmdScEpjRGFQQmx4THIxQjB6ZEcwQy84THpiSmlKcnNodXZ5MWd6bEE5K3JEOG1Ja3V5SUpqRkUzL2Rxbll3b1NtN0lVRVBvRC93dXQ4aUlndVNJRGpsRnhlL3lmWEw1dnVTSTIxQlRaTExoWG9PSUxNTzhIeHdhL0w4YkkwTGZtVWRoR293YjJadlQwZTU3cEZORGdCMDZJbFZ5am1tSUJsMlQvbmw5Unc2U0Q5R2dTRy9RMHVRa2FXM1hobW92S1EzZUZRNE4yVW85T1ExZUZac05lcmY3dlArck80cmhtWTFMZzN2RlZvUDgrOEJYZzFzRm53Ym5DazROVGhXOEd1aUtCRGRrVlZ0VE5Gdk5lbFZzTnFUYnlXbklPTTJvZVRSb3lXdndtcEpIZy91Y1hCcmNKdVhUNER3cnB3WmkydnkwVkN4OFl0WGcvRDJiVTRPZml1UTNlRmZFMktENGJmQ3FpTE5COTkzZ1hzR2x3YTJDVDROekJhY0dJVlE2WXNpcFFkaDB4RWRPRFVLakl4clNwODhvblo4emJiRkxnMURvaUZPNUJYdkRHdjJNeTkvSmhVVDhKVVpUSTB5RGFOSExCeklidnFURE5ZaFVpVncva2RqUTFrTTJDSEZEUGpLVytLenlSVEYwZy9nYTl3OXkrZkFOUXB4dlg4Q1UrTnk3RlVXRGVGM1krZzNsUk9JZjRrMFVEWDllQ3l2TzUzMVB5WWhIZ2E5enZQWkpVNWI3M1kvZVhqOEh2OUQ0OG42SGFGNUxiY2pSdDhUWlR0ZGE1TTFEZlhuYmtYMUMwU0hDRkt6UUI1RmU4b3A0R05HTkhhdnZaRVNiVndUNXI2VzF4eXVDUEJZM1k5WWdEcXprbkgvZTNZZk56enVMMzBsMEllYnJaNWtLdHVESVh0MW44NjhFVDZrZjMvNDl0THZyQ2NaeUY4UHUyMTVkQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFjUEliTnJCaE9hQlh1Y29BQUFBQVNVVk9SSzVDWUlJPSI+CjxzY3JpcHQ+Ly8gQ29weXJpZ2h0IDIwMTQgR29vZ2xlIEluYy4gQWxsIHJpZ2h0cyByZXNlcnZlZC4KLy8KLy8gTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlICJMaWNlbnNlIik7Ci8vIHlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS4KLy8gICAgIFlvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdAovLwovLyBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjAKLy8KLy8gVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZQovLyBkaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiAiQVMgSVMiIEJBU0lTLAovLyBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC4KLy8gICAgIFNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmQKLy8gbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuCgohZnVuY3Rpb24oYSxiKXt2YXIgYz17fSxkPXt9LGU9e30sZj1udWxsOyFmdW5jdGlvbihhLGIpe2Z1bmN0aW9uIGMoYSl7aWYoIm51bWJlciI9PXR5cGVvZiBhKXJldHVybiBhO3ZhciBiPXt9O2Zvcih2YXIgYyBpbiBhKWJbY109YVtjXTtyZXR1cm4gYn1mdW5jdGlvbiBkKCl7dGhpcy5fZGVsYXk9MCx0aGlzLl9lbmREZWxheT0wLHRoaXMuX2ZpbGw9Im5vbmUiLHRoaXMuX2l0ZXJhdGlvblN0YXJ0PTAsdGhpcy5faXRlcmF0aW9ucz0xLHRoaXMuX2R1cmF0aW9uPTAsdGhpcy5fcGxheWJhY2tSYXRlPTEsdGhpcy5fZGlyZWN0aW9uPSJub3JtYWwiLHRoaXMuX2Vhc2luZz0ibGluZWFyIix0aGlzLl9lYXNpbmdGdW5jdGlvbj13fWZ1bmN0aW9uIGUoKXtyZXR1cm4gYS5pc0RlcHJlY2F0ZWQoIkludmFsaWQgdGltaW5nIGlucHV0cyIsIjIwMTYtMDMtMDIiLCJUeXBlRXJyb3IgZXhjZXB0aW9ucyB3aWxsIGJlIHRocm93biBpbnN0ZWFkLiIsITApfWZ1bmN0aW9uIGYoYixjLGUpe3ZhciBmPW5ldyBkO3JldHVybiBjJiYoZi5maWxsPSJib3RoIixmLmR1cmF0aW9uPSJhdXRvIiksIm51bWJlciIhPXR5cGVvZiBifHxpc05hTihiKT92b2lkIDAhPT1iJiZPYmplY3QuZ2V0T3duUHJvcGVydHlOYW1lcyhiKS5mb3JFYWNoKGZ1bmN0aW9uKGMpe2lmKCJhdXRvIiE9YltjXSl7aWYoKCJudW1iZXIiPT10eXBlb2YgZltjXXx8ImR1cmF0aW9uIj09YykmJigibnVtYmVyIiE9dHlwZW9mIGJbY118fGlzTmFOKGJbY10pKSlyZXR1cm47aWYoImZpbGwiPT1jJiYtMT09dS5pbmRleE9mKGJbY10pKXJldHVybjtpZigiZGlyZWN0aW9uIj09YyYmLTE9PXYuaW5kZXhPZihiW2NdKSlyZXR1cm47aWYoInBsYXliYWNrUmF0ZSI9PWMmJjEhPT1iW2NdJiZhLmlzRGVwcmVjYXRlZCgiQW5pbWF0aW9uRWZmZWN0VGltaW5nLnBsYXliYWNrUmF0ZSIsIjIwMTQtMTEtMjgiLCJVc2UgQW5pbWF0aW9uLnBsYXliYWNrUmF0ZSBpbnN0ZWFkLiIpKXJldHVybjtmW2NdPWJbY119fSk6Zi5kdXJhdGlvbj1iLGZ9ZnVuY3Rpb24gZyhhKXtyZXR1cm4ibnVtYmVyIj09dHlwZW9mIGEmJihhPWlzTmFOKGEpP3tkdXJhdGlvbjowfTp7ZHVyYXRpb246YX0pLGF9ZnVuY3Rpb24gaChiLGMpe3JldHVybiBiPWEubnVtZXJpY1RpbWluZ1RvT2JqZWN0KGIpLGYoYixjKX1mdW5jdGlvbiBpKGEsYixjLGQpe3JldHVybiAwPmF8fGE+MXx8MD5jfHxjPjE/dzpmdW5jdGlvbihlKXtmdW5jdGlvbiBmKGEsYixjKXtyZXR1cm4gMyphKigxLWMpKigxLWMpKmMrMypiKigxLWMpKmMqYytjKmMqY31pZigwPT1lfHwxPT1lKXJldHVybiBlO2Zvcih2YXIgZz0wLGg9MTs7KXt2YXIgaT0oZytoKS8yLGo9ZihhLGMsaSk7aWYoTWF0aC5hYnMoZS1qKTwxZS00KXJldHVybiBmKGIsZCxpKTtlPmo/Zz1pOmg9aX19fWZ1bmN0aW9uIGooYSxiKXtyZXR1cm4gZnVuY3Rpb24oYyl7aWYoYz49MSlyZXR1cm4gMTt2YXIgZD0xL2E7cmV0dXJuIGMrPWIqZCxjLWMlZH19ZnVuY3Rpb24gayhhKXtCfHwoQj1kb2N1bWVudC5jcmVhdGVFbGVtZW50KCJkaXYiKS5zdHlsZSksQi5hbmltYXRpb25UaW1pbmdGdW5jdGlvbj0iIixCLmFuaW1hdGlvblRpbWluZ0Z1bmN0aW9uPWE7dmFyIGI9Qi5hbmltYXRpb25UaW1pbmdGdW5jdGlvbjtpZigiIj09YiYmZSgpKXRocm93IG5ldyBUeXBlRXJyb3IoYSsiIGlzIG5vdCBhIHZhbGlkIHZhbHVlIGZvciBlYXNpbmciKTt2YXIgYz1ELmV4ZWMoYik7aWYoYylyZXR1cm4gaS5hcHBseSh0aGlzLGMuc2xpY2UoMSkubWFwKE51bWJlcikpO3ZhciBkPUUuZXhlYyhiKTtpZihkKXJldHVybiBqKE51bWJlcihkWzFdKSx7c3RhcnQ6eCxtaWRkbGU6eSxlbmQ6en1bZFsyXV0pO3ZhciBmPUFbYl07cmV0dXJuIGY/Zjp3fWZ1bmN0aW9uIGwoYSl7cmV0dXJuIE1hdGguYWJzKG0oYSkvYS5wbGF5YmFja1JhdGUpfWZ1bmN0aW9uIG0oYSl7cmV0dXJuIGEuZHVyYXRpb24qYS5pdGVyYXRpb25zfWZ1bmN0aW9uIG4oYSxiLGMpe3JldHVybiBudWxsPT1iP0Y6YjxjLmRlbGF5P0c6Yj49Yy5kZWxheSthP0g6SX1mdW5jdGlvbiBvKGEsYixjLGQsZSl7c3dpdGNoKGQpe2Nhc2UgRzpyZXR1cm4iYmFja3dhcmRzIj09Ynx8ImJvdGgiPT1iPzA6bnVsbDtjYXNlIEk6cmV0dXJuIGMtZTtjYXNlIEg6cmV0dXJuImZvcndhcmRzIj09Ynx8ImJvdGgiPT1iP2E6bnVsbDtjYXNlIEY6cmV0dXJuIG51bGx9fWZ1bmN0aW9uIHAoYSxiLGMsZCl7cmV0dXJuKGQucGxheWJhY2tSYXRlPDA/Yi1hOmIpKmQucGxheWJhY2tSYXRlK2N9ZnVuY3Rpb24gcShhLGIsYyxkLGUpe3JldHVybiBjPT09MS8wfHxjPT09LSgxLzApfHxjLWQ9PWImJmUuaXRlcmF0aW9ucyYmKGUuaXRlcmF0aW9ucytlLml0ZXJhdGlvblN0YXJ0KSUxPT0wP2E6YyVhfWZ1bmN0aW9uIHIoYSxiLGMsZCl7cmV0dXJuIDA9PT1jPzA6Yj09YT9kLml0ZXJhdGlvblN0YXJ0K2QuaXRlcmF0aW9ucy0xOk1hdGguZmxvb3IoYy9hKX1mdW5jdGlvbiBzKGEsYixjLGQpe3ZhciBlPWElMj49MSxmPSJub3JtYWwiPT1kLmRpcmVjdGlvbnx8ZC5kaXJlY3Rpb249PShlPyJhbHRlcm5hdGUtcmV2ZXJzZSI6ImFsdGVybmF0ZSIpLGc9Zj9jOmItYyxoPWcvYjtyZXR1cm4gYipkLl9lYXNpbmdGdW5jdGlvbihoKX1mdW5jdGlvbiB0KGEsYixjKXt2YXIgZD1uKGEsYixjKSxlPW8oYSxjLmZpbGwsYixkLGMuZGVsYXkpO2lmKG51bGw9PT1lKXJldHVybiBudWxsO2lmKDA9PT1hKXJldHVybiBkPT09Rz8wOjE7dmFyIGY9Yy5pdGVyYXRpb25TdGFydCpjLmR1cmF0aW9uLGc9cChhLGUsZixjKSxoPXEoYy5kdXJhdGlvbixtKGMpLGcsZixjKSxpPXIoYy5kdXJhdGlvbixoLGcsYyk7cmV0dXJuIHMoaSxjLmR1cmF0aW9uLGgsYykvYy5kdXJhdGlvbn12YXIgdT0iYmFja3dhcmRzfGZvcndhcmRzfGJvdGh8bm9uZSIuc3BsaXQoInwiKSx2PSJyZXZlcnNlfGFsdGVybmF0ZXxhbHRlcm5hdGUtcmV2ZXJzZSIuc3BsaXQoInwiKSx3PWZ1bmN0aW9uKGEpe3JldHVybiBhfTtkLnByb3RvdHlwZT17X3NldE1lbWJlcjpmdW5jdGlvbihiLGMpe3RoaXNbIl8iK2JdPWMsdGhpcy5fZWZmZWN0JiYodGhpcy5fZWZmZWN0Ll90aW1pbmdJbnB1dFtiXT1jLHRoaXMuX2VmZmVjdC5fdGltaW5nPWEubm9ybWFsaXplVGltaW5nSW5wdXQodGhpcy5fZWZmZWN0Ll90aW1pbmdJbnB1dCksdGhpcy5fZWZmZWN0LmFjdGl2ZUR1cmF0aW9uPWEuY2FsY3VsYXRlQWN0aXZlRHVyYXRpb24odGhpcy5fZWZmZWN0Ll90aW1pbmcpLHRoaXMuX2VmZmVjdC5fYW5pbWF0aW9uJiZ0aGlzLl9lZmZlY3QuX2FuaW1hdGlvbi5fcmVidWlsZFVuZGVybHlpbmdBbmltYXRpb24oKSl9LGdldCBwbGF5YmFja1JhdGUoKXtyZXR1cm4gdGhpcy5fcGxheWJhY2tSYXRlfSxzZXQgZGVsYXkoYSl7dGhpcy5fc2V0TWVtYmVyKCJkZWxheSIsYSl9LGdldCBkZWxheSgpe3JldHVybiB0aGlzLl9kZWxheX0sc2V0IGVuZERlbGF5KGEpe3RoaXMuX3NldE1lbWJlcigiZW5kRGVsYXkiLGEpfSxnZXQgZW5kRGVsYXkoKXtyZXR1cm4gdGhpcy5fZW5kRGVsYXl9LHNldCBmaWxsKGEpe3RoaXMuX3NldE1lbWJlcigiZmlsbCIsYSl9LGdldCBmaWxsKCl7cmV0dXJuIHRoaXMuX2ZpbGx9LHNldCBpdGVyYXRpb25TdGFydChhKXtpZigoaXNOYU4oYSl8fDA+YSkmJmUoKSl0aHJvdyBuZXcgVHlwZUVycm9yKCJpdGVyYXRpb25TdGFydCBtdXN0IGJlIGEgbm9uLW5lZ2F0aXZlIG51bWJlciwgcmVjZWl2ZWQ6ICIrdGltaW5nLml0ZXJhdGlvblN0YXJ0KTt0aGlzLl9zZXRNZW1iZXIoIml0ZXJhdGlvblN0YXJ0IixhKX0sZ2V0IGl0ZXJhdGlvblN0YXJ0KCl7cmV0dXJuIHRoaXMuX2l0ZXJhdGlvblN0YXJ0fSxzZXQgZHVyYXRpb24oYSl7aWYoImF1dG8iIT1hJiYoaXNOYU4oYSl8fDA+YSkmJmUoKSl0aHJvdyBuZXcgVHlwZUVycm9yKCJkdXJhdGlvbiBtdXN0IGJlIG5vbi1uZWdhdGl2ZSBvciBhdXRvLCByZWNlaXZlZDogIithKTt0aGlzLl9zZXRNZW1iZXIoImR1cmF0aW9uIixhKX0sZ2V0IGR1cmF0aW9uKCl7cmV0dXJuIHRoaXMuX2R1cmF0aW9ufSxzZXQgZGlyZWN0aW9uKGEpe3RoaXMuX3NldE1lbWJlcigiZGlyZWN0aW9uIixhKX0sZ2V0IGRpcmVjdGlvbigpe3JldHVybiB0aGlzLl9kaXJlY3Rpb259LHNldCBlYXNpbmcoYSl7dGhpcy5fZWFzaW5nRnVuY3Rpb249ayhhKSx0aGlzLl9zZXRNZW1iZXIoImVhc2luZyIsYSl9LGdldCBlYXNpbmcoKXtyZXR1cm4gdGhpcy5fZWFzaW5nfSxzZXQgaXRlcmF0aW9ucyhhKXtpZigoaXNOYU4oYSl8fDA+YSkmJmUoKSl0aHJvdyBuZXcgVHlwZUVycm9yKCJpdGVyYXRpb25zIG11c3QgYmUgbm9uLW5lZ2F0aXZlLCByZWNlaXZlZDogIithKTt0aGlzLl9zZXRNZW1iZXIoIml0ZXJhdGlvbnMiLGEpfSxnZXQgaXRlcmF0aW9ucygpe3JldHVybiB0aGlzLl9pdGVyYXRpb25zfX07dmFyIHg9MSx5PS41LHo9MCxBPXtlYXNlOmkoLjI1LC4xLC4yNSwxKSwiZWFzZS1pbiI6aSguNDIsMCwxLDEpLCJlYXNlLW91dCI6aSgwLDAsLjU4LDEpLCJlYXNlLWluLW91dCI6aSguNDIsMCwuNTgsMSksInN0ZXAtc3RhcnQiOmooMSx4KSwic3RlcC1taWRkbGUiOmooMSx5KSwic3RlcC1lbmQiOmooMSx6KX0sQj1udWxsLEM9IlxccyooLT9cXGQrXFwuP1xcZCp8LT9cXC5cXGQrKVxccyoiLEQ9bmV3IFJlZ0V4cCgiY3ViaWMtYmV6aWVyXFwoIitDKyIsIitDKyIsIitDKyIsIitDKyJcXCkiKSxFPS9zdGVwc1woXHMqKFxkKylccyosXHMqKHN0YXJ0fG1pZGRsZXxlbmQpXHMqXCkvLEY9MCxHPTEsSD0yLEk9MzthLmNsb25lVGltaW5nSW5wdXQ9YyxhLm1ha2VUaW1pbmc9ZixhLm51bWVyaWNUaW1pbmdUb09iamVjdD1nLGEubm9ybWFsaXplVGltaW5nSW5wdXQ9aCxhLmNhbGN1bGF0ZUFjdGl2ZUR1cmF0aW9uPWwsYS5jYWxjdWxhdGVUaW1lRnJhY3Rpb249dCxhLmNhbGN1bGF0ZVBoYXNlPW4sYS50b1RpbWluZ0Z1bmN0aW9uPWt9KGMsZiksZnVuY3Rpb24oYSxiKXtmdW5jdGlvbiBjKGEsYil7cmV0dXJuIGEgaW4gaj9qW2FdW2JdfHxiOmJ9ZnVuY3Rpb24gZChhLGIsZCl7dmFyIGU9Z1thXTtpZihlKXtoLnN0eWxlW2FdPWI7Zm9yKHZhciBmIGluIGUpe3ZhciBpPWVbZl0saj1oLnN0eWxlW2ldO2RbaV09YyhpLGopfX1lbHNlIGRbYV09YyhhLGIpfWZ1bmN0aW9uIGUoYSl7dmFyIGI9W107Zm9yKHZhciBjIGluIGEpaWYoIShjIGluWyJlYXNpbmciLCJvZmZzZXQiLCJjb21wb3NpdGUiXSkpe3ZhciBkPWFbY107QXJyYXkuaXNBcnJheShkKXx8KGQ9W2RdKTtmb3IodmFyIGUsZj1kLmxlbmd0aCxnPTA7Zj5nO2crKyllPXt9LCJvZmZzZXQiaW4gYT9lLm9mZnNldD1hLm9mZnNldDoxPT1mP2Uub2Zmc2V0PTE6ZS5vZmZzZXQ9Zy8oZi0xKSwiZWFzaW5nImluIGEmJihlLmVhc2luZz1hLmVhc2luZyksImNvbXBvc2l0ZSJpbiBhJiYoZS5jb21wb3NpdGU9YS5jb21wb3NpdGUpLGVbY109ZFtnXSxiLnB1c2goZSl9cmV0dXJuIGIuc29ydChmdW5jdGlvbihhLGIpe3JldHVybiBhLm9mZnNldC1iLm9mZnNldH0pLGJ9ZnVuY3Rpb24gZihhKXtmdW5jdGlvbiBiKCl7dmFyIGE9Yy5sZW5ndGg7bnVsbD09Y1thLTFdLm9mZnNldCYmKGNbYS0xXS5vZmZzZXQ9MSksYT4xJiZudWxsPT1jWzBdLm9mZnNldCYmKGNbMF0ub2Zmc2V0PTApO2Zvcih2YXIgYj0wLGQ9Y1swXS5vZmZzZXQsZT0xO2E+ZTtlKyspe3ZhciBmPWNbZV0ub2Zmc2V0O2lmKG51bGwhPWYpe2Zvcih2YXIgZz0xO2UtYj5nO2crKyljW2IrZ10ub2Zmc2V0PWQrKGYtZCkqZy8oZS1iKTtiPWUsZD1mfX19aWYobnVsbD09YSlyZXR1cm5bXTt3aW5kb3cuU3ltYm9sJiZTeW1ib2wuaXRlcmF0b3ImJkFycmF5LnByb3RvdHlwZS5mcm9tJiZhW1N5bWJvbC5pdGVyYXRvcl0mJihhPUFycmF5LmZyb20oYSkpLEFycmF5LmlzQXJyYXkoYSl8fChhPWUoYSkpO2Zvcih2YXIgYz1hLm1hcChmdW5jdGlvbihhKXt2YXIgYj17fTtmb3IodmFyIGMgaW4gYSl7dmFyIGU9YVtjXTtpZigib2Zmc2V0Ij09Yyl7aWYobnVsbCE9ZSYmKGU9TnVtYmVyKGUpLCFpc0Zpbml0ZShlKSkpdGhyb3cgbmV3IFR5cGVFcnJvcigia2V5ZnJhbWUgb2Zmc2V0cyBtdXN0IGJlIG51bWJlcnMuIil9ZWxzZXtpZigiY29tcG9zaXRlIj09Yyl0aHJvd3t0eXBlOkRPTUV4Y2VwdGlvbi5OT1RfU1VQUE9SVEVEX0VSUixuYW1lOiJOb3RTdXBwb3J0ZWRFcnJvciIsbWVzc2FnZToiYWRkIGNvbXBvc2l0aW5nIGlzIG5vdCBzdXBwb3J0ZWQifTtlPSIiK2V9ZChjLGUsYil9cmV0dXJuIHZvaWQgMD09Yi5vZmZzZXQmJihiLm9mZnNldD1udWxsKSxifSksZj0hMCxnPS0oMS8wKSxoPTA7aDxjLmxlbmd0aDtoKyspe3ZhciBpPWNbaF0ub2Zmc2V0O2lmKG51bGwhPWkpe2lmKGc+aSl0aHJvd3tjb2RlOkRPTUV4Y2VwdGlvbi5JTlZBTElEX01PRElGSUNBVElPTl9FUlIsbmFtZToiSW52YWxpZE1vZGlmaWNhdGlvbkVycm9yIixtZXNzYWdlOiJLZXlmcmFtZXMgYXJlIG5vdCBsb29zZWx5IHNvcnRlZCBieSBvZmZzZXQuIFNvcnQgb3Igc3BlY2lmeSBvZmZzZXRzLiJ9O2c9aX1lbHNlIGY9ITF9cmV0dXJuIGM9Yy5maWx0ZXIoZnVuY3Rpb24oYSl7cmV0dXJuIGEub2Zmc2V0Pj0wJiZhLm9mZnNldDw9MX0pLGZ8fGIoKSxjfXZhciBnPXtiYWNrZ3JvdW5kOlsiYmFja2dyb3VuZEltYWdlIiwiYmFja2dyb3VuZFBvc2l0aW9uIiwiYmFja2dyb3VuZFNpemUiLCJiYWNrZ3JvdW5kUmVwZWF0IiwiYmFja2dyb3VuZEF0dGFjaG1lbnQiLCJiYWNrZ3JvdW5kT3JpZ2luIiwiYmFja2dyb3VuZENsaXAiLCJiYWNrZ3JvdW5kQ29sb3IiXSxib3JkZXI6WyJib3JkZXJUb3BDb2xvciIsImJvcmRlclRvcFN0eWxlIiwiYm9yZGVyVG9wV2lkdGgiLCJib3JkZXJSaWdodENvbG9yIiwiYm9yZGVyUmlnaHRTdHlsZSIsImJvcmRlclJpZ2h0V2lkdGgiLCJib3JkZXJCb3R0b21Db2xvciIsImJvcmRlckJvdHRvbVN0eWxlIiwiYm9yZGVyQm90dG9tV2lkdGgiLCJib3JkZXJMZWZ0Q29sb3IiLCJib3JkZXJMZWZ0U3R5bGUiLCJib3JkZXJMZWZ0V2lkdGgiXSxib3JkZXJCb3R0b206WyJib3JkZXJCb3R0b21XaWR0aCIsImJvcmRlckJvdHRvbVN0eWxlIiwiYm9yZGVyQm90dG9tQ29sb3IiXSxib3JkZXJDb2xvcjpbImJvcmRlclRvcENvbG9yIiwiYm9yZGVyUmlnaHRDb2xvciIsImJvcmRlckJvdHRvbUNvbG9yIiwiYm9yZGVyTGVmdENvbG9yIl0sYm9yZGVyTGVmdDpbImJvcmRlckxlZnRXaWR0aCIsImJvcmRlckxlZnRTdHlsZSIsImJvcmRlckxlZnRDb2xvciJdLGJvcmRlclJhZGl1czpbImJvcmRlclRvcExlZnRSYWRpdXMiLCJib3JkZXJUb3BSaWdodFJhZGl1cyIsImJvcmRlckJvdHRvbVJpZ2h0UmFkaXVzIiwiYm9yZGVyQm90dG9tTGVmdFJhZGl1cyJdLGJvcmRlclJpZ2h0OlsiYm9yZGVyUmlnaHRXaWR0aCIsImJvcmRlclJpZ2h0U3R5bGUiLCJib3JkZXJSaWdodENvbG9yIl0sYm9yZGVyVG9wOlsiYm9yZGVyVG9wV2lkdGgiLCJib3JkZXJUb3BTdHlsZSIsImJvcmRlclRvcENvbG9yIl0sYm9yZGVyV2lkdGg6WyJib3JkZXJUb3BXaWR0aCIsImJvcmRlclJpZ2h0V2lkdGgiLCJib3JkZXJCb3R0b21XaWR0aCIsImJvcmRlckxlZnRXaWR0aCJdLGZsZXg6WyJmbGV4R3JvdyIsImZsZXhTaHJpbmsiLCJmbGV4QmFzaXMiXSxmb250OlsiZm9udEZhbWlseSIsImZvbnRTaXplIiwiZm9udFN0eWxlIiwiZm9udFZhcmlhbnQiLCJmb250V2VpZ2h0IiwibGluZUhlaWdodCJdLG1hcmdpbjpbIm1hcmdpblRvcCIsIm1hcmdpblJpZ2h0IiwibWFyZ2luQm90dG9tIiwibWFyZ2luTGVmdCJdLG91dGxpbmU6WyJvdXRsaW5lQ29sb3IiLCJvdXRsaW5lU3R5bGUiLCJvdXRsaW5lV2lkdGgiXSxwYWRkaW5nOlsicGFkZGluZ1RvcCIsInBhZGRpbmdSaWdodCIsInBhZGRpbmdCb3R0b20iLCJwYWRkaW5nTGVmdCJdfSxoPWRvY3VtZW50LmNyZWF0ZUVsZW1lbnROUygiaHR0cDovL3d3dy53My5vcmcvMTk5OS94aHRtbCIsImRpdiIpLGk9e3RoaW46IjFweCIsbWVkaXVtOiIzcHgiLHRoaWNrOiI1cHgifSxqPXtib3JkZXJCb3R0b21XaWR0aDppLGJvcmRlckxlZnRXaWR0aDppLGJvcmRlclJpZ2h0V2lkdGg6aSxib3JkZXJUb3BXaWR0aDppLGZvbnRTaXplOnsieHgtc21hbGwiOiI2MCUiLCJ4LXNtYWxsIjoiNzUlIixzbWFsbDoiODklIixtZWRpdW06IjEwMCUiLGxhcmdlOiIxMjAlIiwieC1sYXJnZSI6IjE1MCUiLCJ4eC1sYXJnZSI6IjIwMCUifSxmb250V2VpZ2h0Ontub3JtYWw6IjQwMCIsYm9sZDoiNzAwIn0sb3V0bGluZVdpZHRoOmksdGV4dFNoYWRvdzp7bm9uZToiMHB4IDBweCAwcHggdHJhbnNwYXJlbnQifSxib3hTaGFkb3c6e25vbmU6IjBweCAwcHggMHB4IDBweCB0cmFuc3BhcmVudCJ9fTthLmNvbnZlcnRUb0FycmF5Rm9ybT1lLGEubm9ybWFsaXplS2V5ZnJhbWVzPWZ9KGMsZiksZnVuY3Rpb24oYSl7dmFyIGI9e307YS5pc0RlcHJlY2F0ZWQ9ZnVuY3Rpb24oYSxjLGQsZSl7dmFyIGY9ZT8iYXJlIjoiaXMiLGc9bmV3IERhdGUsaD1uZXcgRGF0ZShjKTtyZXR1cm4gaC5zZXRNb250aChoLmdldE1vbnRoKCkrMyksaD5nPyhhIGluIGJ8fGNvbnNvbGUud2FybigiV2ViIEFuaW1hdGlvbnM6ICIrYSsiICIrZisiIGRlcHJlY2F0ZWQgYW5kIHdpbGwgc3RvcCB3b3JraW5nIG9uICIraC50b0RhdGVTdHJpbmcoKSsiLiAiK2QpLGJbYV09ITAsITEpOiEwfSxhLmRlcHJlY2F0ZWQ9ZnVuY3Rpb24oYixjLGQsZSl7dmFyIGY9ZT8iYXJlIjoiaXMiO2lmKGEuaXNEZXByZWNhdGVkKGIsYyxkLGUpKXRocm93IG5ldyBFcnJvcihiKyIgIitmKyIgbm8gbG9uZ2VyIHN1cHBvcnRlZC4gIitkKX19KGMpLGZ1bmN0aW9uKCl7aWYoZG9jdW1lbnQuZG9jdW1lbnRFbGVtZW50LmFuaW1hdGUpe3ZhciBhPWRvY3VtZW50LmRvY3VtZW50RWxlbWVudC5hbmltYXRlKFtdLDApLGI9ITA7aWYoYSYmKGI9ITEsInBsYXl8Y3VycmVudFRpbWV8cGF1c2V8cmV2ZXJzZXxwbGF5YmFja1JhdGV8Y2FuY2VsfGZpbmlzaHxzdGFydFRpbWV8cGxheVN0YXRlIi5zcGxpdCgifCIpLmZvckVhY2goZnVuY3Rpb24oYyl7dm9pZCAwPT09YVtjXSYmKGI9ITApfSkpLCFiKXJldHVybn0hZnVuY3Rpb24oYSxiLGMpe2Z1bmN0aW9uIGQoYSl7Zm9yKHZhciBiPXt9LGM9MDtjPGEubGVuZ3RoO2MrKylmb3IodmFyIGQgaW4gYVtjXSlpZigib2Zmc2V0IiE9ZCYmImVhc2luZyIhPWQmJiJjb21wb3NpdGUiIT1kKXt2YXIgZT17b2Zmc2V0OmFbY10ub2Zmc2V0LGVhc2luZzphW2NdLmVhc2luZyx2YWx1ZTphW2NdW2RdfTtiW2RdPWJbZF18fFtdLGJbZF0ucHVzaChlKX1mb3IodmFyIGYgaW4gYil7dmFyIGc9YltmXTtpZigwIT1nWzBdLm9mZnNldHx8MSE9Z1tnLmxlbmd0aC0xXS5vZmZzZXQpdGhyb3d7dHlwZTpET01FeGNlcHRpb24uTk9UX1NVUFBPUlRFRF9FUlIsbmFtZToiTm90U3VwcG9ydGVkRXJyb3IiLG1lc3NhZ2U6IlBhcnRpYWwga2V5ZnJhbWVzIGFyZSBub3Qgc3VwcG9ydGVkIn19cmV0dXJuIGJ9ZnVuY3Rpb24gZShjKXt2YXIgZD1bXTtmb3IodmFyIGUgaW4gYylmb3IodmFyIGY9Y1tlXSxnPTA7ZzxmLmxlbmd0aC0xO2crKyl7dmFyIGg9ZltnXS5vZmZzZXQsaT1mW2crMV0ub2Zmc2V0LGo9ZltnXS52YWx1ZSxrPWZbZysxXS52YWx1ZSxsPWZbZ10uZWFzaW5nO2g9PWkmJigxPT1pP2o9azprPWopLGQucHVzaCh7c3RhcnRUaW1lOmgsZW5kVGltZTppLGVhc2luZzphLnRvVGltaW5nRnVuY3Rpb24obD9sOiJsaW5lYXIiKSxwcm9wZXJ0eTplLGludGVycG9sYXRpb246Yi5wcm9wZXJ0eUludGVycG9sYXRpb24oZSxqLGspfSl9cmV0dXJuIGQuc29ydChmdW5jdGlvbihhLGIpe3JldHVybiBhLnN0YXJ0VGltZS1iLnN0YXJ0VGltZX0pLGR9Yi5jb252ZXJ0RWZmZWN0SW5wdXQ9ZnVuY3Rpb24oYyl7dmFyIGY9YS5ub3JtYWxpemVLZXlmcmFtZXMoYyksZz1kKGYpLGg9ZShnKTtyZXR1cm4gZnVuY3Rpb24oYSxjKXtpZihudWxsIT1jKWguZmlsdGVyKGZ1bmN0aW9uKGEpe3JldHVybiAwPj1jJiYwPT1hLnN0YXJ0VGltZXx8Yz49MSYmMT09YS5lbmRUaW1lfHxjPj1hLnN0YXJ0VGltZSYmYzw9YS5lbmRUaW1lfSkuZm9yRWFjaChmdW5jdGlvbihkKXt2YXIgZT1jLWQuc3RhcnRUaW1lLGY9ZC5lbmRUaW1lLWQuc3RhcnRUaW1lLGc9MD09Zj8wOmQuZWFzaW5nKGUvZik7Yi5hcHBseShhLGQucHJvcGVydHksZC5pbnRlcnBvbGF0aW9uKGcpKX0pO2Vsc2UgZm9yKHZhciBkIGluIGcpIm9mZnNldCIhPWQmJiJlYXNpbmciIT1kJiYiY29tcG9zaXRlIiE9ZCYmYi5jbGVhcihhLGQpfX19KGMsZCxmKSxmdW5jdGlvbihhLGIsYyl7ZnVuY3Rpb24gZChhKXtyZXR1cm4gYS5yZXBsYWNlKC8tKC4pL2csZnVuY3Rpb24oYSxiKXtyZXR1cm4gYi50b1VwcGVyQ2FzZSgpfSl9ZnVuY3Rpb24gZShhLGIsYyl7aFtjXT1oW2NdfHxbXSxoW2NdLnB1c2goW2EsYl0pfWZ1bmN0aW9uIGYoYSxiLGMpe2Zvcih2YXIgZj0wO2Y8Yy5sZW5ndGg7ZisrKXt2YXIgZz1jW2ZdO2UoYSxiLGQoZykpfX1mdW5jdGlvbiBnKGMsZSxmKXt2YXIgZz1jOy8tLy50ZXN0KGMpJiYhYS5pc0RlcHJlY2F0ZWQoIkh5cGhlbmF0ZWQgcHJvcGVydHkgbmFtZXMiLCIyMDE2LTAzLTIyIiwiVXNlIGNhbWVsQ2FzZSBpbnN0ZWFkLiIsITApJiYoZz1kKGMpKSwiaW5pdGlhbCIhPWUmJiJpbml0aWFsIiE9Znx8KCJpbml0aWFsIj09ZSYmKGU9aVtnXSksImluaXRpYWwiPT1mJiYoZj1pW2ddKSk7Zm9yKHZhciBqPWU9PWY/W106aFtnXSxrPTA7aiYmazxqLmxlbmd0aDtrKyspe3ZhciBsPWpba11bMF0oZSksbT1qW2tdWzBdKGYpO2lmKHZvaWQgMCE9PWwmJnZvaWQgMCE9PW0pe3ZhciBuPWpba11bMV0obCxtKTtpZihuKXt2YXIgbz1iLkludGVycG9sYXRpb24uYXBwbHkobnVsbCxuKTtyZXR1cm4gZnVuY3Rpb24oYSl7cmV0dXJuIDA9PWE/ZToxPT1hP2Y6byhhKX19fX1yZXR1cm4gYi5JbnRlcnBvbGF0aW9uKCExLCEwLGZ1bmN0aW9uKGEpe3JldHVybiBhP2Y6ZX0pfXZhciBoPXt9O2IuYWRkUHJvcGVydGllc0hhbmRsZXI9Zjt2YXIgaT17YmFja2dyb3VuZENvbG9yOiJ0cmFuc3BhcmVudCIsYmFja2dyb3VuZFBvc2l0aW9uOiIwJSAwJSIsYm9yZGVyQm90dG9tQ29sb3I6ImN1cnJlbnRDb2xvciIsYm9yZGVyQm90dG9tTGVmdFJhZGl1czoiMHB4Iixib3JkZXJCb3R0b21SaWdodFJhZGl1czoiMHB4Iixib3JkZXJCb3R0b21XaWR0aDoiM3B4Iixib3JkZXJMZWZ0Q29sb3I6ImN1cnJlbnRDb2xvciIsYm9yZGVyTGVmdFdpZHRoOiIzcHgiLGJvcmRlclJpZ2h0Q29sb3I6ImN1cnJlbnRDb2xvciIsYm9yZGVyUmlnaHRXaWR0aDoiM3B4Iixib3JkZXJTcGFjaW5nOiIycHgiLGJvcmRlclRvcENvbG9yOiJjdXJyZW50Q29sb3IiLGJvcmRlclRvcExlZnRSYWRpdXM6IjBweCIsYm9yZGVyVG9wUmlnaHRSYWRpdXM6IjBweCIsYm9yZGVyVG9wV2lkdGg6IjNweCIsYm90dG9tOiJhdXRvIixjbGlwOiJyZWN0KDBweCwgMHB4LCAwcHgsIDBweCkiLGNvbG9yOiJibGFjayIsZm9udFNpemU6IjEwMCUiLGZvbnRXZWlnaHQ6IjQwMCIsaGVpZ2h0OiJhdXRvIixsZWZ0OiJhdXRvIixsZXR0ZXJTcGFjaW5nOiJub3JtYWwiLGxpbmVIZWlnaHQ6IjEyMCUiLG1hcmdpbkJvdHRvbToiMHB4IixtYXJnaW5MZWZ0OiIwcHgiLG1hcmdpblJpZ2h0OiIwcHgiLG1hcmdpblRvcDoiMHB4IixtYXhIZWlnaHQ6Im5vbmUiLG1heFdpZHRoOiJub25lIixtaW5IZWlnaHQ6IjBweCIsbWluV2lkdGg6IjBweCIsb3BhY2l0eToiMS4wIixvdXRsaW5lQ29sb3I6ImludmVydCIsb3V0bGluZU9mZnNldDoiMHB4IixvdXRsaW5lV2lkdGg6IjNweCIscGFkZGluZ0JvdHRvbToiMHB4IixwYWRkaW5nTGVmdDoiMHB4IixwYWRkaW5nUmlnaHQ6IjBweCIscGFkZGluZ1RvcDoiMHB4IixyaWdodDoiYXV0byIsdGV4dEluZGVudDoiMHB4Iix0ZXh0U2hhZG93OiIwcHggMHB4IDBweCB0cmFuc3BhcmVudCIsdG9wOiJhdXRvIix0cmFuc2Zvcm06IiIsdmVydGljYWxBbGlnbjoiMHB4Iix2aXNpYmlsaXR5OiJ2aXNpYmxlIix3aWR0aDoiYXV0byIsd29yZFNwYWNpbmc6Im5vcm1hbCIsekluZGV4OiJhdXRvIn07Yi5wcm9wZXJ0eUludGVycG9sYXRpb249Z30oYyxkLGYpLGZ1bmN0aW9uKGEsYixjKXtmdW5jdGlvbiBkKGIpe3ZhciBjPWEuY2FsY3VsYXRlQWN0aXZlRHVyYXRpb24oYiksZD1mdW5jdGlvbihkKXtyZXR1cm4gYS5jYWxjdWxhdGVUaW1lRnJhY3Rpb24oYyxkLGIpfTtyZXR1cm4gZC5fdG90YWxEdXJhdGlvbj1iLmRlbGF5K2MrYi5lbmREZWxheSxkLl9pc0N1cnJlbnQ9ZnVuY3Rpb24oZCl7dmFyIGU9YS5jYWxjdWxhdGVQaGFzZShjLGQsYik7cmV0dXJuIGU9PT1QaGFzZUFjdGl2ZXx8ZT09PVBoYXNlQmVmb3JlfSxkfWIuS2V5ZnJhbWVFZmZlY3Q9ZnVuY3Rpb24oYyxlLGYsZyl7dmFyIGgsaT1kKGEubm9ybWFsaXplVGltaW5nSW5wdXQoZikpLGo9Yi5jb252ZXJ0RWZmZWN0SW5wdXQoZSksaz1mdW5jdGlvbigpe2ooYyxoKX07cmV0dXJuIGsuX3VwZGF0ZT1mdW5jdGlvbihhKXtyZXR1cm4gaD1pKGEpLG51bGwhPT1ofSxrLl9jbGVhcj1mdW5jdGlvbigpe2ooYyxudWxsKX0say5faGFzU2FtZVRhcmdldD1mdW5jdGlvbihhKXtyZXR1cm4gYz09PWF9LGsuX2lzQ3VycmVudD1pLl9pc0N1cnJlbnQsay5fdG90YWxEdXJhdGlvbj1pLl90b3RhbER1cmF0aW9uLGsuX2lkPWcsa30sYi5OdWxsRWZmZWN0PWZ1bmN0aW9uKGEpe3ZhciBiPWZ1bmN0aW9uKCl7YSYmKGEoKSxhPW51bGwpfTtyZXR1cm4gYi5fdXBkYXRlPWZ1bmN0aW9uKCl7cmV0dXJuIG51bGx9LGIuX3RvdGFsRHVyYXRpb249MCxiLl9pc0N1cnJlbnQ9ZnVuY3Rpb24oKXtyZXR1cm4hMX0sYi5faGFzU2FtZVRhcmdldD1mdW5jdGlvbigpe3JldHVybiExfSxifX0oYyxkLGYpLGZ1bmN0aW9uKGEsYil7YS5hcHBseT1mdW5jdGlvbihiLGMsZCl7Yi5zdHlsZVthLnByb3BlcnR5TmFtZShjKV09ZH0sYS5jbGVhcj1mdW5jdGlvbihiLGMpe2Iuc3R5bGVbYS5wcm9wZXJ0eU5hbWUoYyldPSIifX0oZCxmKSxmdW5jdGlvbihhKXt3aW5kb3cuRWxlbWVudC5wcm90b3R5cGUuYW5pbWF0ZT1mdW5jdGlvbihiLGMpe3ZhciBkPSIiO3JldHVybiBjJiZjLmlkJiYoZD1jLmlkKSxhLnRpbWVsaW5lLl9wbGF5KGEuS2V5ZnJhbWVFZmZlY3QodGhpcyxiLGMsZCkpfX0oZCksZnVuY3Rpb24oYSxiKXtmdW5jdGlvbiBjKGEsYixkKXtpZigibnVtYmVyIj09dHlwZW9mIGEmJiJudW1iZXIiPT10eXBlb2YgYilyZXR1cm4gYSooMS1kKStiKmQ7aWYoImJvb2xlYW4iPT10eXBlb2YgYSYmImJvb2xlYW4iPT10eXBlb2YgYilyZXR1cm4uNT5kP2E6YjtpZihhLmxlbmd0aD09Yi5sZW5ndGgpe2Zvcih2YXIgZT1bXSxmPTA7ZjxhLmxlbmd0aDtmKyspZS5wdXNoKGMoYVtmXSxiW2ZdLGQpKTtyZXR1cm4gZX10aHJvdyJNaXNtYXRjaGVkIGludGVycG9sYXRpb24gYXJndW1lbnRzICIrYSsiOiIrYn1hLkludGVycG9sYXRpb249ZnVuY3Rpb24oYSxiLGQpe3JldHVybiBmdW5jdGlvbihlKXtyZXR1cm4gZChjKGEsYixlKSl9fX0oZCxmKSxmdW5jdGlvbihhLGIsYyl7YS5zZXF1ZW5jZU51bWJlcj0wO3ZhciBkPWZ1bmN0aW9uKGEsYixjKXt0aGlzLnRhcmdldD1hLHRoaXMuY3VycmVudFRpbWU9Yix0aGlzLnRpbWVsaW5lVGltZT1jLHRoaXMudHlwZT0iZmluaXNoIix0aGlzLmJ1YmJsZXM9ITEsdGhpcy5jYW5jZWxhYmxlPSExLHRoaXMuY3VycmVudFRhcmdldD1hLHRoaXMuZGVmYXVsdFByZXZlbnRlZD0hMSx0aGlzLmV2ZW50UGhhc2U9RXZlbnQuQVRfVEFSR0VULHRoaXMudGltZVN0YW1wPURhdGUubm93KCl9O2IuQW5pbWF0aW9uPWZ1bmN0aW9uKGIpe3RoaXMuaWQ9IiIsYiYmYi5faWQmJih0aGlzLmlkPWIuX2lkKSx0aGlzLl9zZXF1ZW5jZU51bWJlcj1hLnNlcXVlbmNlTnVtYmVyKyssdGhpcy5fY3VycmVudFRpbWU9MCx0aGlzLl9zdGFydFRpbWU9bnVsbCx0aGlzLl9wYXVzZWQ9ITEsdGhpcy5fcGxheWJhY2tSYXRlPTEsdGhpcy5faW5UaW1lbGluZT0hMCx0aGlzLl9maW5pc2hlZEZsYWc9ITAsdGhpcy5vbmZpbmlzaD1udWxsLHRoaXMuX2ZpbmlzaEhhbmRsZXJzPVtdLHRoaXMuX2VmZmVjdD1iLHRoaXMuX2luRWZmZWN0PXRoaXMuX2VmZmVjdC5fdXBkYXRlKDApLHRoaXMuX2lkbGU9ITAsdGhpcy5fY3VycmVudFRpbWVQZW5kaW5nPSExfSxiLkFuaW1hdGlvbi5wcm90b3R5cGU9e19lbnN1cmVBbGl2ZTpmdW5jdGlvbigpe3RoaXMucGxheWJhY2tSYXRlPDAmJjA9PT10aGlzLmN1cnJlbnRUaW1lP3RoaXMuX2luRWZmZWN0PXRoaXMuX2VmZmVjdC5fdXBkYXRlKC0xKTp0aGlzLl9pbkVmZmVjdD10aGlzLl9lZmZlY3QuX3VwZGF0ZSh0aGlzLmN1cnJlbnRUaW1lKSx0aGlzLl9pblRpbWVsaW5lfHwhdGhpcy5faW5FZmZlY3QmJnRoaXMuX2ZpbmlzaGVkRmxhZ3x8KHRoaXMuX2luVGltZWxpbmU9ITAsYi50aW1lbGluZS5fYW5pbWF0aW9ucy5wdXNoKHRoaXMpKX0sX3RpY2tDdXJyZW50VGltZTpmdW5jdGlvbihhLGIpe2EhPXRoaXMuX2N1cnJlbnRUaW1lJiYodGhpcy5fY3VycmVudFRpbWU9YSx0aGlzLl9pc0ZpbmlzaGVkJiYhYiYmKHRoaXMuX2N1cnJlbnRUaW1lPXRoaXMuX3BsYXliYWNrUmF0ZT4wP3RoaXMuX3RvdGFsRHVyYXRpb246MCksdGhpcy5fZW5zdXJlQWxpdmUoKSl9LGdldCBjdXJyZW50VGltZSgpe3JldHVybiB0aGlzLl9pZGxlfHx0aGlzLl9jdXJyZW50VGltZVBlbmRpbmc/bnVsbDp0aGlzLl9jdXJyZW50VGltZX0sc2V0IGN1cnJlbnRUaW1lKGEpe2E9K2EsaXNOYU4oYSl8fChiLnJlc3RhcnQoKSx0aGlzLl9wYXVzZWR8fG51bGw9PXRoaXMuX3N0YXJ0VGltZXx8KHRoaXMuX3N0YXJ0VGltZT10aGlzLl90aW1lbGluZS5jdXJyZW50VGltZS1hL3RoaXMuX3BsYXliYWNrUmF0ZSksdGhpcy5fY3VycmVudFRpbWVQZW5kaW5nPSExLHRoaXMuX2N1cnJlbnRUaW1lIT1hJiYodGhpcy5fdGlja0N1cnJlbnRUaW1lKGEsITApLGIuaW52YWxpZGF0ZUVmZmVjdHMoKSkpfSxnZXQgc3RhcnRUaW1lKCl7cmV0dXJuIHRoaXMuX3N0YXJ0VGltZX0sc2V0IHN0YXJ0VGltZShhKXthPSthLGlzTmFOKGEpfHx0aGlzLl9wYXVzZWR8fHRoaXMuX2lkbGV8fCh0aGlzLl9zdGFydFRpbWU9YSx0aGlzLl90aWNrQ3VycmVudFRpbWUoKHRoaXMuX3RpbWVsaW5lLmN1cnJlbnRUaW1lLXRoaXMuX3N0YXJ0VGltZSkqdGhpcy5wbGF5YmFja1JhdGUpLGIuaW52YWxpZGF0ZUVmZmVjdHMoKSl9LGdldCBwbGF5YmFja1JhdGUoKXtyZXR1cm4gdGhpcy5fcGxheWJhY2tSYXRlfSxzZXQgcGxheWJhY2tSYXRlKGEpe2lmKGEhPXRoaXMuX3BsYXliYWNrUmF0ZSl7dmFyIGI9dGhpcy5jdXJyZW50VGltZTt0aGlzLl9wbGF5YmFja1JhdGU9YSx0aGlzLl9zdGFydFRpbWU9bnVsbCwicGF1c2VkIiE9dGhpcy5wbGF5U3RhdGUmJiJpZGxlIiE9dGhpcy5wbGF5U3RhdGUmJnRoaXMucGxheSgpLG51bGwhPWImJih0aGlzLmN1cnJlbnRUaW1lPWIpfX0sZ2V0IF9pc0ZpbmlzaGVkKCl7cmV0dXJuIXRoaXMuX2lkbGUmJih0aGlzLl9wbGF5YmFja1JhdGU+MCYmdGhpcy5fY3VycmVudFRpbWU+PXRoaXMuX3RvdGFsRHVyYXRpb258fHRoaXMuX3BsYXliYWNrUmF0ZTwwJiZ0aGlzLl9jdXJyZW50VGltZTw9MCl9LGdldCBfdG90YWxEdXJhdGlvbigpe3JldHVybiB0aGlzLl9lZmZlY3QuX3RvdGFsRHVyYXRpb259LGdldCBwbGF5U3RhdGUoKXtyZXR1cm4gdGhpcy5faWRsZT8iaWRsZSI6bnVsbD09dGhpcy5fc3RhcnRUaW1lJiYhdGhpcy5fcGF1c2VkJiYwIT10aGlzLnBsYXliYWNrUmF0ZXx8dGhpcy5fY3VycmVudFRpbWVQZW5kaW5nPyJwZW5kaW5nIjp0aGlzLl9wYXVzZWQ/InBhdXNlZCI6dGhpcy5faXNGaW5pc2hlZD8iZmluaXNoZWQiOiJydW5uaW5nIn0scGxheTpmdW5jdGlvbigpe3RoaXMuX3BhdXNlZD0hMSwodGhpcy5faXNGaW5pc2hlZHx8dGhpcy5faWRsZSkmJih0aGlzLl9jdXJyZW50VGltZT10aGlzLl9wbGF5YmFja1JhdGU+MD8wOnRoaXMuX3RvdGFsRHVyYXRpb24sdGhpcy5fc3RhcnRUaW1lPW51bGwpLHRoaXMuX2ZpbmlzaGVkRmxhZz0hMSx0aGlzLl9pZGxlPSExLHRoaXMuX2Vuc3VyZUFsaXZlKCksYi5pbnZhbGlkYXRlRWZmZWN0cygpfSxwYXVzZTpmdW5jdGlvbigpe3RoaXMuX2lzRmluaXNoZWR8fHRoaXMuX3BhdXNlZHx8dGhpcy5faWRsZXx8KHRoaXMuX2N1cnJlbnRUaW1lUGVuZGluZz0hMCksdGhpcy5fc3RhcnRUaW1lPW51bGwsdGhpcy5fcGF1c2VkPSEwfSxmaW5pc2g6ZnVuY3Rpb24oKXt0aGlzLl9pZGxlfHwodGhpcy5jdXJyZW50VGltZT10aGlzLl9wbGF5YmFja1JhdGU+MD90aGlzLl90b3RhbER1cmF0aW9uOjAsdGhpcy5fc3RhcnRUaW1lPXRoaXMuX3RvdGFsRHVyYXRpb24tdGhpcy5jdXJyZW50VGltZSx0aGlzLl9jdXJyZW50VGltZVBlbmRpbmc9ITEsYi5pbnZhbGlkYXRlRWZmZWN0cygpKX0sY2FuY2VsOmZ1bmN0aW9uKCl7dGhpcy5faW5FZmZlY3QmJih0aGlzLl9pbkVmZmVjdD0hMSx0aGlzLl9pZGxlPSEwLHRoaXMuX2ZpbmlzaGVkRmxhZz0hMCx0aGlzLmN1cnJlbnRUaW1lPTAsdGhpcy5fc3RhcnRUaW1lPW51bGwsdGhpcy5fZWZmZWN0Ll91cGRhdGUobnVsbCksYi5pbnZhbGlkYXRlRWZmZWN0cygpKX0scmV2ZXJzZTpmdW5jdGlvbigpe3RoaXMucGxheWJhY2tSYXRlKj0tMSx0aGlzLnBsYXkoKX0sYWRkRXZlbnRMaXN0ZW5lcjpmdW5jdGlvbihhLGIpeyJmdW5jdGlvbiI9PXR5cGVvZiBiJiYiZmluaXNoIj09YSYmdGhpcy5fZmluaXNoSGFuZGxlcnMucHVzaChiKX0scmVtb3ZlRXZlbnRMaXN0ZW5lcjpmdW5jdGlvbihhLGIpe2lmKCJmaW5pc2giPT1hKXt2YXIgYz10aGlzLl9maW5pc2hIYW5kbGVycy5pbmRleE9mKGIpO2M+PTAmJnRoaXMuX2ZpbmlzaEhhbmRsZXJzLnNwbGljZShjLDEpfX0sX2ZpcmVFdmVudHM6ZnVuY3Rpb24oYSl7aWYodGhpcy5faXNGaW5pc2hlZCl7aWYoIXRoaXMuX2ZpbmlzaGVkRmxhZyl7dmFyIGI9bmV3IGQodGhpcyx0aGlzLl9jdXJyZW50VGltZSxhKSxjPXRoaXMuX2ZpbmlzaEhhbmRsZXJzLmNvbmNhdCh0aGlzLm9uZmluaXNoP1t0aGlzLm9uZmluaXNoXTpbXSk7c2V0VGltZW91dChmdW5jdGlvbigpe2MuZm9yRWFjaChmdW5jdGlvbihhKXthLmNhbGwoYi50YXJnZXQsYil9KX0sMCksdGhpcy5fZmluaXNoZWRGbGFnPSEwfX1lbHNlIHRoaXMuX2ZpbmlzaGVkRmxhZz0hMX0sX3RpY2s6ZnVuY3Rpb24oYSxiKXt0aGlzLl9pZGxlfHx0aGlzLl9wYXVzZWR8fChudWxsPT10aGlzLl9zdGFydFRpbWU/YiYmKHRoaXMuc3RhcnRUaW1lPWEtdGhpcy5fY3VycmVudFRpbWUvdGhpcy5wbGF5YmFja1JhdGUpOnRoaXMuX2lzRmluaXNoZWR8fHRoaXMuX3RpY2tDdXJyZW50VGltZSgoYS10aGlzLl9zdGFydFRpbWUpKnRoaXMucGxheWJhY2tSYXRlKSksYiYmKHRoaXMuX2N1cnJlbnRUaW1lUGVuZGluZz0hMSx0aGlzLl9maXJlRXZlbnRzKGEpKX0sZ2V0IF9uZWVkc1RpY2soKXtyZXR1cm4gdGhpcy5wbGF5U3RhdGUgaW57cGVuZGluZzoxLHJ1bm5pbmc6MX18fCF0aGlzLl9maW5pc2hlZEZsYWd9fX0oYyxkLGYpLGZ1bmN0aW9uKGEsYixjKXtmdW5jdGlvbiBkKGEpe3ZhciBiPWo7aj1bXSxhPHAuY3VycmVudFRpbWUmJihhPXAuY3VycmVudFRpbWUpLGgoYSwhMCksYi5mb3JFYWNoKGZ1bmN0aW9uKGIpe2JbMV0oYSl9KSxnKCksbD12b2lkIDB9ZnVuY3Rpb24gZShhLGIpe3JldHVybiBhLl9zZXF1ZW5jZU51bWJlci1iLl9zZXF1ZW5jZU51bWJlcn1mdW5jdGlvbiBmKCl7dGhpcy5fYW5pbWF0aW9ucz1bXSx0aGlzLmN1cnJlbnRUaW1lPXdpbmRvdy5wZXJmb3JtYW5jZSYmcGVyZm9ybWFuY2Uubm93P3BlcmZvcm1hbmNlLm5vdygpOjB9ZnVuY3Rpb24gZygpe28uZm9yRWFjaChmdW5jdGlvbihhKXthKCl9KSxvLmxlbmd0aD0wfWZ1bmN0aW9uIGgoYSxjKXtuPSExO3ZhciBkPWIudGltZWxpbmU7ZC5jdXJyZW50VGltZT1hLGQuX2FuaW1hdGlvbnMuc29ydChlKSxtPSExO3ZhciBmPWQuX2FuaW1hdGlvbnM7ZC5fYW5pbWF0aW9ucz1bXTt2YXIgZz1bXSxoPVtdO2Y9Zi5maWx0ZXIoZnVuY3Rpb24oYil7Yi5fdGljayhhLGMpLGIuX2luRWZmZWN0P2gucHVzaChiLl9lZmZlY3QpOmcucHVzaChiLl9lZmZlY3QpLGIuX25lZWRzVGljayYmKG09ITApO3ZhciBkPWIuX2luRWZmZWN0fHxiLl9uZWVkc1RpY2s7cmV0dXJuIGIuX2luVGltZWxpbmU9ZCxkfSksby5wdXNoLmFwcGx5KG8sZyksby5wdXNoLmFwcGx5KG8saCksZC5fYW5pbWF0aW9ucy5wdXNoLmFwcGx5KGQuX2FuaW1hdGlvbnMsZiksbSYmcmVxdWVzdEFuaW1hdGlvbkZyYW1lKGZ1bmN0aW9uKCl7fSl9dmFyIGk9d2luZG93LnJlcXVlc3RBbmltYXRpb25GcmFtZSxqPVtdLGs9MDt3aW5kb3cucmVxdWVzdEFuaW1hdGlvbkZyYW1lPWZ1bmN0aW9uKGEpe3ZhciBiPWsrKztyZXR1cm4gMD09ai5sZW5ndGgmJmkoZCksai5wdXNoKFtiLGFdKSxifSx3aW5kb3cuY2FuY2VsQW5pbWF0aW9uRnJhbWU9ZnVuY3Rpb24oYSl7ai5mb3JFYWNoKGZ1bmN0aW9uKGIpe2JbMF09PWEmJihiWzFdPWZ1bmN0aW9uKCl7fSl9KX0sZi5wcm90b3R5cGU9e19wbGF5OmZ1bmN0aW9uKGMpe2MuX3RpbWluZz1hLm5vcm1hbGl6ZVRpbWluZ0lucHV0KGMudGltaW5nKTt2YXIgZD1uZXcgYi5BbmltYXRpb24oYyk7cmV0dXJuIGQuX2lkbGU9ITEsZC5fdGltZWxpbmU9dGhpcyx0aGlzLl9hbmltYXRpb25zLnB1c2goZCksYi5yZXN0YXJ0KCksYi5pbnZhbGlkYXRlRWZmZWN0cygpLGR9fTt2YXIgbD12b2lkIDAsbT0hMSxuPSExO2IucmVzdGFydD1mdW5jdGlvbigpe3JldHVybiBtfHwobT0hMCxyZXF1ZXN0QW5pbWF0aW9uRnJhbWUoZnVuY3Rpb24oKXt9KSxuPSEwKSxufSxiLmludmFsaWRhdGVFZmZlY3RzPWZ1bmN0aW9uKCl7aChiLnRpbWVsaW5lLmN1cnJlbnRUaW1lLCExKSxnKCl9O3ZhciBvPVtdLHA9bmV3IGY7Yi50aW1lbGluZT1wfShjLGQsZiksZnVuY3Rpb24oYSl7ZnVuY3Rpb24gYihhLGIpe3ZhciBjPWEuZXhlYyhiKTtyZXR1cm4gYz8oYz1hLmlnbm9yZUNhc2U/Y1swXS50b0xvd2VyQ2FzZSgpOmNbMF0sW2MsYi5zdWJzdHIoYy5sZW5ndGgpXSk6dm9pZCAwfWZ1bmN0aW9uIGMoYSxiKXtiPWIucmVwbGFjZSgvXlxzKi8sIiIpO3ZhciBjPWEoYik7cmV0dXJuIGM/W2NbMF0sY1sxXS5yZXBsYWNlKC9eXHMqLywiIildOnZvaWQgMH1mdW5jdGlvbiBkKGEsZCxlKXthPWMuYmluZChudWxsLGEpO2Zvcih2YXIgZj1bXTs7KXt2YXIgZz1hKGUpO2lmKCFnKXJldHVybltmLGVdO2lmKGYucHVzaChnWzBdKSxlPWdbMV0sZz1iKGQsZSksIWd8fCIiPT1nWzFdKXJldHVybltmLGVdO2U9Z1sxXX19ZnVuY3Rpb24gZShhLGIpe2Zvcih2YXIgYz0wLGQ9MDtkPGIubGVuZ3RoJiYoIS9cc3wsLy50ZXN0KGJbZF0pfHwwIT1jKTtkKyspaWYoIigiPT1iW2RdKWMrKztlbHNlIGlmKCIpIj09YltkXSYmKGMtLSwwPT1jJiZkKyssMD49YykpYnJlYWs7dmFyIGU9YShiLnN1YnN0cigwLGQpKTtyZXR1cm4gdm9pZCAwPT1lP3ZvaWQgMDpbZSxiLnN1YnN0cihkKV19ZnVuY3Rpb24gZihhLGIpe2Zvcih2YXIgYz1hLGQ9YjtjJiZkOyljPmQ/YyU9ZDpkJT1jO3JldHVybiBjPWEqYi8oYytkKX1mdW5jdGlvbiBnKGEpe3JldHVybiBmdW5jdGlvbihiKXt2YXIgYz1hKGIpO3JldHVybiBjJiYoY1swXT12b2lkIDApLGN9fWZ1bmN0aW9uIGgoYSxiKXtyZXR1cm4gZnVuY3Rpb24oYyl7dmFyIGQ9YShjKTtyZXR1cm4gZD9kOltiLGNdfX1mdW5jdGlvbiBpKGIsYyl7Zm9yKHZhciBkPVtdLGU9MDtlPGIubGVuZ3RoO2UrKyl7dmFyIGY9YS5jb25zdW1lVHJpbW1lZChiW2VdLGMpO2lmKCFmfHwiIj09ZlswXSlyZXR1cm47dm9pZCAwIT09ZlswXSYmZC5wdXNoKGZbMF0pLGM9ZlsxXX1yZXR1cm4iIj09Yz9kOnZvaWQgMH1mdW5jdGlvbiBqKGEsYixjLGQsZSl7Zm9yKHZhciBnPVtdLGg9W10saT1bXSxqPWYoZC5sZW5ndGgsZS5sZW5ndGgpLGs9MDtqPms7aysrKXt2YXIgbD1iKGRbayVkLmxlbmd0aF0sZVtrJWUubGVuZ3RoXSk7aWYoIWwpcmV0dXJuO2cucHVzaChsWzBdKSxoLnB1c2gobFsxXSksaS5wdXNoKGxbMl0pfXJldHVybltnLGgsZnVuY3Rpb24oYil7dmFyIGQ9Yi5tYXAoZnVuY3Rpb24oYSxiKXtyZXR1cm4gaVtiXShhKX0pLmpvaW4oYyk7cmV0dXJuIGE/YShkKTpkfV19ZnVuY3Rpb24gayhhLGIsYyl7Zm9yKHZhciBkPVtdLGU9W10sZj1bXSxnPTAsaD0wO2g8Yy5sZW5ndGg7aCsrKWlmKCJmdW5jdGlvbiI9PXR5cGVvZiBjW2hdKXt2YXIgaT1jW2hdKGFbZ10sYltnKytdKTtkLnB1c2goaVswXSksZS5wdXNoKGlbMV0pLGYucHVzaChpWzJdKX1lbHNlIWZ1bmN0aW9uKGEpe2QucHVzaCghMSksZS5wdXNoKCExKSxmLnB1c2goZnVuY3Rpb24oKXtyZXR1cm4gY1thXX0pfShoKTtyZXR1cm5bZCxlLGZ1bmN0aW9uKGEpe2Zvcih2YXIgYj0iIixjPTA7YzxhLmxlbmd0aDtjKyspYis9ZltjXShhW2NdKTtyZXR1cm4gYn1dfWEuY29uc3VtZVRva2VuPWIsYS5jb25zdW1lVHJpbW1lZD1jLGEuY29uc3VtZVJlcGVhdGVkPWQsYS5jb25zdW1lUGFyZW50aGVzaXNlZD1lLGEuaWdub3JlPWcsYS5vcHRpb25hbD1oLGEuY29uc3VtZUxpc3Q9aSxhLm1lcmdlTmVzdGVkUmVwZWF0ZWQ9ai5iaW5kKG51bGwsbnVsbCksYS5tZXJnZVdyYXBwZWROZXN0ZWRSZXBlYXRlZD1qLGEubWVyZ2VMaXN0PWt9KGQpLGZ1bmN0aW9uKGEpe2Z1bmN0aW9uIGIoYil7ZnVuY3Rpb24gYyhiKXt2YXIgYz1hLmNvbnN1bWVUb2tlbigvXmluc2V0L2ksYik7aWYoYylyZXR1cm4gZC5pbnNldD0hMCxjO3ZhciBjPWEuY29uc3VtZUxlbmd0aE9yUGVyY2VudChiKTtpZihjKXJldHVybiBkLmxlbmd0aHMucHVzaChjWzBdKSxjO3ZhciBjPWEuY29uc3VtZUNvbG9yKGIpO3JldHVybiBjPyhkLmNvbG9yPWNbMF0sYyk6dm9pZCAwfXZhciBkPXtpbnNldDohMSxsZW5ndGhzOltdLGNvbG9yOm51bGx9LGU9YS5jb25zdW1lUmVwZWF0ZWQoYywvXi8sYik7cmV0dXJuIGUmJmVbMF0ubGVuZ3RoP1tkLGVbMV1dOnZvaWQgMH1mdW5jdGlvbiBjKGMpe3ZhciBkPWEuY29uc3VtZVJlcGVhdGVkKGIsL14sLyxjKTtyZXR1cm4gZCYmIiI9PWRbMV0/ZFswXTp2b2lkIDB9ZnVuY3Rpb24gZChiLGMpe2Zvcig7Yi5sZW5ndGhzLmxlbmd0aDxNYXRoLm1heChiLmxlbmd0aHMubGVuZ3RoLGMubGVuZ3Rocy5sZW5ndGgpOyliLmxlbmd0aHMucHVzaCh7cHg6MH0pO2Zvcig7Yy5sZW5ndGhzLmxlbmd0aDxNYXRoLm1heChiLmxlbmd0aHMubGVuZ3RoLGMubGVuZ3Rocy5sZW5ndGgpOyljLmxlbmd0aHMucHVzaCh7cHg6MH0pO2lmKGIuaW5zZXQ9PWMuaW5zZXQmJiEhYi5jb2xvcj09ISFjLmNvbG9yKXtmb3IodmFyIGQsZT1bXSxmPVtbXSwwXSxnPVtbXSwwXSxoPTA7aDxiLmxlbmd0aHMubGVuZ3RoO2grKyl7dmFyIGk9YS5tZXJnZURpbWVuc2lvbnMoYi5sZW5ndGhzW2hdLGMubGVuZ3Roc1toXSwyPT1oKTtmWzBdLnB1c2goaVswXSksZ1swXS5wdXNoKGlbMV0pLGUucHVzaChpWzJdKX1pZihiLmNvbG9yJiZjLmNvbG9yKXt2YXIgaj1hLm1lcmdlQ29sb3JzKGIuY29sb3IsYy5jb2xvcik7ZlsxXT1qWzBdLGdbMV09alsxXSxkPWpbMl19cmV0dXJuW2YsZyxmdW5jdGlvbihhKXtmb3IodmFyIGM9Yi5pbnNldD8iaW5zZXQgIjoiICIsZj0wO2Y8ZS5sZW5ndGg7ZisrKWMrPWVbZl0oYVswXVtmXSkrIiAiO3JldHVybiBkJiYoYys9ZChhWzFdKSksY31dfX1mdW5jdGlvbiBlKGIsYyxkLGUpe2Z1bmN0aW9uIGYoYSl7cmV0dXJue2luc2V0OmEsY29sb3I6WzAsMCwwLDBdLGxlbmd0aHM6W3tweDowfSx7cHg6MH0se3B4OjB9LHtweDowfV19fWZvcih2YXIgZz1bXSxoPVtdLGk9MDtpPGQubGVuZ3RofHxpPGUubGVuZ3RoO2krKyl7dmFyIGo9ZFtpXXx8ZihlW2ldLmluc2V0KSxrPWVbaV18fGYoZFtpXS5pbnNldCk7Zy5wdXNoKGopLGgucHVzaChrKX1yZXR1cm4gYS5tZXJnZU5lc3RlZFJlcGVhdGVkKGIsYyxnLGgpfXZhciBmPWUuYmluZChudWxsLGQsIiwgIik7YS5hZGRQcm9wZXJ0aWVzSGFuZGxlcihjLGYsWyJib3gtc2hhZG93IiwidGV4dC1zaGFkb3ciXSl9KGQpLGZ1bmN0aW9uKGEsYil7ZnVuY3Rpb24gYyhhKXtyZXR1cm4gYS50b0ZpeGVkKDMpLnJlcGxhY2UoIi4wMDAiLCIiKX1mdW5jdGlvbiBkKGEsYixjKXtyZXR1cm4gTWF0aC5taW4oYixNYXRoLm1heChhLGMpKX1mdW5jdGlvbiBlKGEpe3JldHVybi9eXHMqWy0rXT8oXGQqXC4pP1xkK1xzKiQvLnRlc3QoYSk/TnVtYmVyKGEpOnZvaWQgMH1mdW5jdGlvbiBmKGEsYil7cmV0dXJuW2EsYixjXX1mdW5jdGlvbiBnKGEsYil7cmV0dXJuIDAhPWE/aSgwLDEvMCkoYSxiKTp2b2lkIDB9ZnVuY3Rpb24gaChhLGIpe3JldHVyblthLGIsZnVuY3Rpb24oYSl7cmV0dXJuIE1hdGgucm91bmQoZCgxLDEvMCxhKSl9XX1mdW5jdGlvbiBpKGEsYil7cmV0dXJuIGZ1bmN0aW9uKGUsZil7cmV0dXJuW2UsZixmdW5jdGlvbihlKXtyZXR1cm4gYyhkKGEsYixlKSl9XX19ZnVuY3Rpb24gaihhLGIpe3JldHVyblthLGIsTWF0aC5yb3VuZF19YS5jbGFtcD1kLGEuYWRkUHJvcGVydGllc0hhbmRsZXIoZSxpKDAsMS8wKSxbImJvcmRlci1pbWFnZS13aWR0aCIsImxpbmUtaGVpZ2h0Il0pLGEuYWRkUHJvcGVydGllc0hhbmRsZXIoZSxpKDAsMSksWyJvcGFjaXR5Iiwic2hhcGUtaW1hZ2UtdGhyZXNob2xkIl0pLGEuYWRkUHJvcGVydGllc0hhbmRsZXIoZSxnLFsiZmxleC1ncm93IiwiZmxleC1zaHJpbmsiXSksYS5hZGRQcm9wZXJ0aWVzSGFuZGxlcihlLGgsWyJvcnBoYW5zIiwid2lkb3dzIl0pLGEuYWRkUHJvcGVydGllc0hhbmRsZXIoZSxqLFsiei1pbmRleCJdKSxhLnBhcnNlTnVtYmVyPWUsYS5tZXJnZU51bWJlcnM9ZixhLm51bWJlclRvU3RyaW5nPWN9KGQsZiksZnVuY3Rpb24oYSxiKXtmdW5jdGlvbiBjKGEsYil7cmV0dXJuInZpc2libGUiPT1hfHwidmlzaWJsZSI9PWI/WzAsMSxmdW5jdGlvbihjKXtyZXR1cm4gMD49Yz9hOmM+PTE/YjoidmlzaWJsZSJ9XTp2b2lkIDB9YS5hZGRQcm9wZXJ0aWVzSGFuZGxlcihTdHJpbmcsYyxbInZpc2liaWxpdHkiXSl9KGQpLGZ1bmN0aW9uKGEsYil7ZnVuY3Rpb24gYyhhKXthPWEudHJpbSgpLGYuZmlsbFN0eWxlPSIjMDAwIixmLmZpbGxTdHlsZT1hO3ZhciBiPWYuZmlsbFN0eWxlO2lmKGYuZmlsbFN0eWxlPSIjZmZmIixmLmZpbGxTdHlsZT1hLGI9PWYuZmlsbFN0eWxlKXtmLmZpbGxSZWN0KDAsMCwxLDEpO3ZhciBjPWYuZ2V0SW1hZ2VEYXRhKDAsMCwxLDEpLmRhdGE7Zi5jbGVhclJlY3QoMCwwLDEsMSk7dmFyIGQ9Y1szXS8yNTU7cmV0dXJuW2NbMF0qZCxjWzFdKmQsY1syXSpkLGRdfX1mdW5jdGlvbiBkKGIsYyl7cmV0dXJuW2IsYyxmdW5jdGlvbihiKXtmdW5jdGlvbiBjKGEpe3JldHVybiBNYXRoLm1heCgwLE1hdGgubWluKDI1NSxhKSl9aWYoYlszXSlmb3IodmFyIGQ9MDszPmQ7ZCsrKWJbZF09TWF0aC5yb3VuZChjKGJbZF0vYlszXSkpO3JldHVybiBiWzNdPWEubnVtYmVyVG9TdHJpbmcoYS5jbGFtcCgwLDEsYlszXSkpLCJyZ2JhKCIrYi5qb2luKCIsIikrIikifV19dmFyIGU9ZG9jdW1lbnQuY3JlYXRlRWxlbWVudE5TKCJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hodG1sIiwiY2FudmFzIik7ZS53aWR0aD1lLmhlaWdodD0xO3ZhciBmPWUuZ2V0Q29udGV4dCgiMmQiKTthLmFkZFByb3BlcnRpZXNIYW5kbGVyKGMsZCxbImJhY2tncm91bmQtY29sb3IiLCJib3JkZXItYm90dG9tLWNvbG9yIiwiYm9yZGVyLWxlZnQtY29sb3IiLCJib3JkZXItcmlnaHQtY29sb3IiLCJib3JkZXItdG9wLWNvbG9yIiwiY29sb3IiLCJvdXRsaW5lLWNvbG9yIiwidGV4dC1kZWNvcmF0aW9uLWNvbG9yIl0pLGEuY29uc3VtZUNvbG9yPWEuY29uc3VtZVBhcmVudGhlc2lzZWQuYmluZChudWxsLGMpLGEubWVyZ2VDb2xvcnM9ZH0oZCxmKSxmdW5jdGlvbihhLGIpe2Z1bmN0aW9uIGMoYSxiKXtpZihiPWIudHJpbSgpLnRvTG93ZXJDYXNlKCksIjAiPT1iJiYicHgiLnNlYXJjaChhKT49MClyZXR1cm57cHg6MH07aWYoL15bXihdKiR8XmNhbGMvLnRlc3QoYikpe2I9Yi5yZXBsYWNlKC9jYWxjXCgvZywiKCIpO3ZhciBjPXt9O2I9Yi5yZXBsYWNlKGEsZnVuY3Rpb24oYSl7cmV0dXJuIGNbYV09bnVsbCwiVSIrYX0pO2Zvcih2YXIgZD0iVSgiK2Euc291cmNlKyIpIixlPWIucmVwbGFjZSgvWy0rXT8oXGQqXC4pP1xkKy9nLCJOIikucmVwbGFjZShuZXcgUmVnRXhwKCJOIitkLCJnIiksIkQiKS5yZXBsYWNlKC9cc1srLV1ccy9nLCJPIikucmVwbGFjZSgvXHMvZywiIiksZj1bL05cKihEKS9nLC8oTnxEKVsqXC9dTi9nLC8oTnxEKU9cMS9nLC9cKChOfEQpXCkvZ10sZz0wO2c8Zi5sZW5ndGg7KWZbZ10udGVzdChlKT8oZT1lLnJlcGxhY2UoZltnXSwiJDEiKSxnPTApOmcrKztpZigiRCI9PWUpe2Zvcih2YXIgaCBpbiBjKXt2YXIgaT1ldmFsKGIucmVwbGFjZShuZXcgUmVnRXhwKCJVIitoLCJnIiksIiIpLnJlcGxhY2UobmV3IFJlZ0V4cChkLCJnIiksIiowIikpO2lmKCFpc0Zpbml0ZShpKSlyZXR1cm47Y1toXT1pfXJldHVybiBjfX19ZnVuY3Rpb24gZChhLGIpe3JldHVybiBlKGEsYiwhMCl9ZnVuY3Rpb24gZShiLGMsZCl7dmFyIGUsZj1bXTtmb3IoZSBpbiBiKWYucHVzaChlKTtmb3IoZSBpbiBjKWYuaW5kZXhPZihlKTwwJiZmLnB1c2goZSk7cmV0dXJuIGI9Zi5tYXAoZnVuY3Rpb24oYSl7cmV0dXJuIGJbYV18fDB9KSxjPWYubWFwKGZ1bmN0aW9uKGEpe3JldHVybiBjW2FdfHwwfSksW2IsYyxmdW5jdGlvbihiKXt2YXIgYz1iLm1hcChmdW5jdGlvbihjLGUpe3JldHVybiAxPT1iLmxlbmd0aCYmZCYmKGM9TWF0aC5tYXgoYywwKSksYS5udW1iZXJUb1N0cmluZyhjKStmW2VdfSkuam9pbigiICsgIik7cmV0dXJuIGIubGVuZ3RoPjE/ImNhbGMoIitjKyIpIjpjfV19dmFyIGY9InB4fGVtfGV4fGNofHJlbXx2d3x2aHx2bWlufHZtYXh8Y218bW18aW58cHR8cGMiLGc9Yy5iaW5kKG51bGwsbmV3IFJlZ0V4cChmLCJnIikpLGg9Yy5iaW5kKG51bGwsbmV3IFJlZ0V4cChmKyJ8JSIsImciKSksaT1jLmJpbmQobnVsbCwvZGVnfHJhZHxncmFkfHR1cm4vZyk7YS5wYXJzZUxlbmd0aD1nLGEucGFyc2VMZW5ndGhPclBlcmNlbnQ9aCxhLmNvbnN1bWVMZW5ndGhPclBlcmNlbnQ9YS5jb25zdW1lUGFyZW50aGVzaXNlZC5iaW5kKG51bGwsaCksYS5wYXJzZUFuZ2xlPWksYS5tZXJnZURpbWVuc2lvbnM9ZTt2YXIgaj1hLmNvbnN1bWVQYXJlbnRoZXNpc2VkLmJpbmQobnVsbCxnKSxrPWEuY29uc3VtZVJlcGVhdGVkLmJpbmQodm9pZCAwLGosL14vKSxsPWEuY29uc3VtZVJlcGVhdGVkLmJpbmQodm9pZCAwLGssL14sLyk7YS5jb25zdW1lU2l6ZVBhaXJMaXN0PWw7dmFyIG09ZnVuY3Rpb24oYSl7dmFyIGI9bChhKTtyZXR1cm4gYiYmIiI9PWJbMV0/YlswXTp2b2lkIDB9LG49YS5tZXJnZU5lc3RlZFJlcGVhdGVkLmJpbmQodm9pZCAwLGQsIiAiKSxvPWEubWVyZ2VOZXN0ZWRSZXBlYXRlZC5iaW5kKHZvaWQgMCxuLCIsIik7YS5tZXJnZU5vbk5lZ2F0aXZlU2l6ZVBhaXI9bixhLmFkZFByb3BlcnRpZXNIYW5kbGVyKG0sbyxbImJhY2tncm91bmQtc2l6ZSJdKSxhLmFkZFByb3BlcnRpZXNIYW5kbGVyKGgsZCxbImJvcmRlci1ib3R0b20td2lkdGgiLCJib3JkZXItaW1hZ2Utd2lkdGgiLCJib3JkZXItbGVmdC13aWR0aCIsImJvcmRlci1yaWdodC13aWR0aCIsImJvcmRlci10b3Atd2lkdGgiLCJmbGV4LWJhc2lzIiwiZm9udC1zaXplIiwiaGVpZ2h0IiwibGluZS1oZWlnaHQiLCJtYXgtaGVpZ2h0IiwibWF4LXdpZHRoIiwib3V0bGluZS13aWR0aCIsIndpZHRoIl0pLGEuYWRkUHJvcGVydGllc0hhbmRsZXIoaCxlLFsiYm9yZGVyLWJvdHRvbS1sZWZ0LXJhZGl1cyIsImJvcmRlci1ib3R0b20tcmlnaHQtcmFkaXVzIiwiYm9yZGVyLXRvcC1sZWZ0LXJhZGl1cyIsImJvcmRlci10b3AtcmlnaHQtcmFkaXVzIiwiYm90dG9tIiwibGVmdCIsImxldHRlci1zcGFjaW5nIiwibWFyZ2luLWJvdHRvbSIsIm1hcmdpbi1sZWZ0IiwibWFyZ2luLXJpZ2h0IiwibWFyZ2luLXRvcCIsIm1pbi1oZWlnaHQiLCJtaW4td2lkdGgiLCJvdXRsaW5lLW9mZnNldCIsInBhZGRpbmctYm90dG9tIiwicGFkZGluZy1sZWZ0IiwicGFkZGluZy1yaWdodCIsInBhZGRpbmctdG9wIiwicGVyc3BlY3RpdmUiLCJyaWdodCIsInNoYXBlLW1hcmdpbiIsInRleHQtaW5kZW50IiwidG9wIiwidmVydGljYWwtYWxpZ24iLCJ3b3JkLXNwYWNpbmciXSl9KGQsZiksZnVuY3Rpb24oYSxiKXtmdW5jdGlvbiBjKGIpe3JldHVybiBhLmNvbnN1bWVMZW5ndGhPclBlcmNlbnQoYil8fGEuY29uc3VtZVRva2VuKC9eYXV0by8sYil9ZnVuY3Rpb24gZChiKXt2YXIgZD1hLmNvbnN1bWVMaXN0KFthLmlnbm9yZShhLmNvbnN1bWVUb2tlbi5iaW5kKG51bGwsL15yZWN0LykpLGEuaWdub3JlKGEuY29uc3VtZVRva2VuLmJpbmQobnVsbCwvXlwoLykpLGEuY29uc3VtZVJlcGVhdGVkLmJpbmQobnVsbCxjLC9eLC8pLGEuaWdub3JlKGEuY29uc3VtZVRva2VuLmJpbmQobnVsbCwvXlwpLykpXSxiKTtyZXR1cm4gZCYmND09ZFswXS5sZW5ndGg/ZFswXTp2b2lkIDB9ZnVuY3Rpb24gZShiLGMpe3JldHVybiJhdXRvIj09Ynx8ImF1dG8iPT1jP1shMCwhMSxmdW5jdGlvbihkKXt2YXIgZT1kP2I6YztpZigiYXV0byI9PWUpcmV0dXJuImF1dG8iO3ZhciBmPWEubWVyZ2VEaW1lbnNpb25zKGUsZSk7cmV0dXJuIGZbMl0oZlswXSl9XTphLm1lcmdlRGltZW5zaW9ucyhiLGMpfWZ1bmN0aW9uIGYoYSl7cmV0dXJuInJlY3QoIithKyIpIn12YXIgZz1hLm1lcmdlV3JhcHBlZE5lc3RlZFJlcGVhdGVkLmJpbmQobnVsbCxmLGUsIiwgIik7YS5wYXJzZUJveD1kLGEubWVyZ2VCb3hlcz1nLGEuYWRkUHJvcGVydGllc0hhbmRsZXIoZCxnLFsiY2xpcCJdKX0oZCxmKSxmdW5jdGlvbihhLGIpe2Z1bmN0aW9uIGMoYSl7cmV0dXJuIGZ1bmN0aW9uKGIpe3ZhciBjPTA7cmV0dXJuIGEubWFwKGZ1bmN0aW9uKGEpe3JldHVybiBhPT09az9iW2MrK106YX0pfX1mdW5jdGlvbiBkKGEpe3JldHVybiBhfWZ1bmN0aW9uIGUoYil7aWYoYj1iLnRvTG93ZXJDYXNlKCkudHJpbSgpLCJub25lIj09YilyZXR1cm5bXTtmb3IodmFyIGMsZD0vXHMqKFx3KylcKChbXildKilcKS9nLGU9W10sZj0wO2M9ZC5leGVjKGIpOyl7aWYoYy5pbmRleCE9ZilyZXR1cm47Zj1jLmluZGV4K2NbMF0ubGVuZ3RoO3ZhciBnPWNbMV0saD1uW2ddO2lmKCFoKXJldHVybjt2YXIgaT1jWzJdLnNwbGl0KCIsIiksaj1oWzBdO2lmKGoubGVuZ3RoPGkubGVuZ3RoKXJldHVybjtmb3IodmFyIGs9W10sbz0wO288ai5sZW5ndGg7bysrKXt2YXIgcCxxPWlbb10scj1qW29dO2lmKHA9cT97QTpmdW5jdGlvbihiKXtyZXR1cm4iMCI9PWIudHJpbSgpP206YS5wYXJzZUFuZ2xlKGIpfSxOOmEucGFyc2VOdW1iZXIsVDphLnBhcnNlTGVuZ3RoT3JQZXJjZW50LEw6YS5wYXJzZUxlbmd0aH1bci50b1VwcGVyQ2FzZSgpXShxKTp7YTptLG46a1swXSx0Omx9W3JdLHZvaWQgMD09PXApcmV0dXJuO2sucHVzaChwKX1pZihlLnB1c2goe3Q6ZyxkOmt9KSxkLmxhc3RJbmRleD09Yi5sZW5ndGgpcmV0dXJuIGV9fWZ1bmN0aW9uIGYoYSl7cmV0dXJuIGEudG9GaXhlZCg2KS5yZXBsYWNlKCIuMDAwMDAwIiwiIil9ZnVuY3Rpb24gZyhiLGMpe2lmKGIuZGVjb21wb3NpdGlvblBhaXIhPT1jKXtiLmRlY29tcG9zaXRpb25QYWlyPWM7dmFyIGQ9YS5tYWtlTWF0cml4RGVjb21wb3NpdGlvbihiKX1pZihjLmRlY29tcG9zaXRpb25QYWlyIT09Yil7Yy5kZWNvbXBvc2l0aW9uUGFpcj1iO3ZhciBlPWEubWFrZU1hdHJpeERlY29tcG9zaXRpb24oYyl9cmV0dXJuIG51bGw9PWRbMF18fG51bGw9PWVbMF0/W1shMV0sWyEwXSxmdW5jdGlvbihhKXtyZXR1cm4gYT9jWzBdLmQ6YlswXS5kfV06KGRbMF0ucHVzaCgwKSxlWzBdLnB1c2goMSksW2QsZSxmdW5jdGlvbihiKXt2YXIgYz1hLnF1YXQoZFswXVszXSxlWzBdWzNdLGJbNV0pLGc9YS5jb21wb3NlTWF0cml4KGJbMF0sYlsxXSxiWzJdLGMsYls0XSksaD1nLm1hcChmKS5qb2luKCIsIik7cmV0dXJuIGh9XSl9ZnVuY3Rpb24gaChhKXtyZXR1cm4gYS5yZXBsYWNlKC9beHldLywiIil9ZnVuY3Rpb24gaShhKXtyZXR1cm4gYS5yZXBsYWNlKC8oeHx5fHp8M2QpPyQvLCIzZCIpfWZ1bmN0aW9uIGooYixjKXt2YXIgZD1hLm1ha2VNYXRyaXhEZWNvbXBvc2l0aW9uJiYhMCxlPSExO2lmKCFiLmxlbmd0aHx8IWMubGVuZ3RoKXtiLmxlbmd0aHx8KGU9ITAsYj1jLGM9W10pO2Zvcih2YXIgZj0wO2Y8Yi5sZW5ndGg7ZisrKXt2YXIgaj1iW2ZdLnQsaz1iW2ZdLmQsbD0ic2NhbGUiPT1qLnN1YnN0cigwLDUpPzE6MDtjLnB1c2goe3Q6aixkOmsubWFwKGZ1bmN0aW9uKGEpe2lmKCJudW1iZXIiPT10eXBlb2YgYSlyZXR1cm4gbDt2YXIgYj17fTtmb3IodmFyIGMgaW4gYSliW2NdPWw7cmV0dXJuIGJ9KX0pfX12YXIgbT1mdW5jdGlvbihhLGIpe3JldHVybiJwZXJzcGVjdGl2ZSI9PWEmJiJwZXJzcGVjdGl2ZSI9PWJ8fCgibWF0cml4Ij09YXx8Im1hdHJpeDNkIj09YSkmJigibWF0cml4Ij09Ynx8Im1hdHJpeDNkIj09Yil9LG89W10scD1bXSxxPVtdO2lmKGIubGVuZ3RoIT1jLmxlbmd0aCl7aWYoIWQpcmV0dXJuO3ZhciByPWcoYixjKTtvPVtyWzBdXSxwPVtyWzFdXSxxPVtbIm1hdHJpeCIsW3JbMl1dXV19ZWxzZSBmb3IodmFyIGY9MDtmPGIubGVuZ3RoO2YrKyl7dmFyIGoscz1iW2ZdLnQsdD1jW2ZdLnQsdT1iW2ZdLmQsdj1jW2ZdLmQsdz1uW3NdLHg9blt0XTtpZihtKHMsdCkpe2lmKCFkKXJldHVybjt2YXIgcj1nKFtiW2ZdXSxbY1tmXV0pO28ucHVzaChyWzBdKSxwLnB1c2goclsxXSkscS5wdXNoKFsibWF0cml4IixbclsyXV1dKX1lbHNle2lmKHM9PXQpaj1zO2Vsc2UgaWYod1syXSYmeFsyXSYmaChzKT09aCh0KSlqPWgocyksdT13WzJdKHUpLHY9eFsyXSh2KTtlbHNle2lmKCF3WzFdfHwheFsxXXx8aShzKSE9aSh0KSl7aWYoIWQpcmV0dXJuO3ZhciByPWcoYixjKTtvPVtyWzBdXSxwPVtyWzFdXSxxPVtbIm1hdHJpeCIsW3JbMl1dXV07YnJlYWt9aj1pKHMpLHU9d1sxXSh1KSx2PXhbMV0odil9Zm9yKHZhciB5PVtdLHo9W10sQT1bXSxCPTA7Qjx1Lmxlbmd0aDtCKyspe3ZhciBDPSJudW1iZXIiPT10eXBlb2YgdVtCXT9hLm1lcmdlTnVtYmVyczphLm1lcmdlRGltZW5zaW9ucyxyPUModVtCXSx2W0JdKTt5W0JdPXJbMF0seltCXT1yWzFdLEEucHVzaChyWzJdKX1vLnB1c2goeSkscC5wdXNoKHopLHEucHVzaChbaixBXSl9fWlmKGUpe3ZhciBEPW87bz1wLHA9RH1yZXR1cm5bbyxwLGZ1bmN0aW9uKGEpe3JldHVybiBhLm1hcChmdW5jdGlvbihhLGIpe3ZhciBjPWEubWFwKGZ1bmN0aW9uKGEsYyl7cmV0dXJuIHFbYl1bMV1bY10oYSl9KS5qb2luKCIsIik7cmV0dXJuIm1hdHJpeCI9PXFbYl1bMF0mJjE2PT1jLnNwbGl0KCIsIikubGVuZ3RoJiYocVtiXVswXT0ibWF0cml4M2QiKSxxW2JdWzBdKyIoIitjKyIpIn0pLmpvaW4oIiAiKX1dfXZhciBrPW51bGwsbD17cHg6MH0sbT17ZGVnOjB9LG49e21hdHJpeDpbIk5OTk5OTiIsW2ssaywwLDAsayxrLDAsMCwwLDAsMSwwLGssaywwLDFdLGRdLG1hdHJpeDNkOlsiTk5OTk5OTk5OTk5OTk5OTiIsZF0scm90YXRlOlsiQSJdLHJvdGF0ZXg6WyJBIl0scm90YXRleTpbIkEiXSxyb3RhdGV6OlsiQSJdLHJvdGF0ZTNkOlsiTk5OQSJdLHBlcnNwZWN0aXZlOlsiTCJdLHNjYWxlOlsiTm4iLGMoW2ssaywxXSksZF0sc2NhbGV4OlsiTiIsYyhbaywxLDFdKSxjKFtrLDFdKV0sc2NhbGV5OlsiTiIsYyhbMSxrLDFdKSxjKFsxLGtdKV0sc2NhbGV6OlsiTiIsYyhbMSwxLGtdKV0sc2NhbGUzZDpbIk5OTiIsZF0sc2tldzpbIkFhIixudWxsLGRdLHNrZXd4OlsiQSIsbnVsbCxjKFtrLG1dKV0sc2tld3k6WyJBIixudWxsLGMoW20sa10pXSx0cmFuc2xhdGU6WyJUdCIsYyhbayxrLGxdKSxkXSx0cmFuc2xhdGV4OlsiVCIsYyhbayxsLGxdKSxjKFtrLGxdKV0sdHJhbnNsYXRleTpbIlQiLGMoW2wsayxsXSksYyhbbCxrXSldLHRyYW5zbGF0ZXo6WyJMIixjKFtsLGwsa10pXSx0cmFuc2xhdGUzZDpbIlRUTCIsZF19O2EuYWRkUHJvcGVydGllc0hhbmRsZXIoZSxqLFsidHJhbnNmb3JtIl0pfShkLGYpLGZ1bmN0aW9uKGEsYil7ZnVuY3Rpb24gYyhhLGIpe2IuY29uY2F0KFthXSkuZm9yRWFjaChmdW5jdGlvbihiKXtiIGluIGRvY3VtZW50LmRvY3VtZW50RWxlbWVudC5zdHlsZSYmKGRbYV09Yil9KX12YXIgZD17fTtjKCJ0cmFuc2Zvcm0iLFsid2Via2l0VHJhbnNmb3JtIiwibXNUcmFuc2Zvcm0iXSksYygidHJhbnNmb3JtT3JpZ2luIixbIndlYmtpdFRyYW5zZm9ybU9yaWdpbiJdKSxjKCJwZXJzcGVjdGl2ZSIsWyJ3ZWJraXRQZXJzcGVjdGl2ZSJdKSxjKCJwZXJzcGVjdGl2ZU9yaWdpbiIsWyJ3ZWJraXRQZXJzcGVjdGl2ZU9yaWdpbiJdKSxhLnByb3BlcnR5TmFtZT1mdW5jdGlvbihhKXtyZXR1cm4gZFthXXx8YX19KGQsZil9KCksIWZ1bmN0aW9uKCl7aWYodm9pZCAwPT09ZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgiZGl2IikuYW5pbWF0ZShbXSkub25jYW5jZWwpe3ZhciBhO2lmKHdpbmRvdy5wZXJmb3JtYW5jZSYmcGVyZm9ybWFuY2Uubm93KXZhciBhPWZ1bmN0aW9uKCl7cmV0dXJuIHBlcmZvcm1hbmNlLm5vdygpfTtlbHNlIHZhciBhPWZ1bmN0aW9uKCl7cmV0dXJuIERhdGUubm93KCl9O3ZhciBiPWZ1bmN0aW9uKGEsYixjKXt0aGlzLnRhcmdldD1hLHRoaXMuY3VycmVudFRpbWU9Yix0aGlzLnRpbWVsaW5lVGltZT1jLHRoaXMudHlwZT0iY2FuY2VsIix0aGlzLmJ1YmJsZXM9ITEsdGhpcy5jYW5jZWxhYmxlPSExLHRoaXMuY3VycmVudFRhcmdldD1hLHRoaXMuZGVmYXVsdFByZXZlbnRlZD0hMSx0aGlzLmV2ZW50UGhhc2U9RXZlbnQuQVRfVEFSR0VULHRoaXMudGltZVN0YW1wPURhdGUubm93KCl9LGM9d2luZG93LkVsZW1lbnQucHJvdG90eXBlLmFuaW1hdGU7d2luZG93LkVsZW1lbnQucHJvdG90eXBlLmFuaW1hdGU9ZnVuY3Rpb24oZCxlKXt2YXIgZj1jLmNhbGwodGhpcyxkLGUpO2YuX2NhbmNlbEhhbmRsZXJzPVtdLGYub25jYW5jZWw9bnVsbDt2YXIgZz1mLmNhbmNlbDtmLmNhbmNlbD1mdW5jdGlvbigpe2cuY2FsbCh0aGlzKTt2YXIgYz1uZXcgYih0aGlzLG51bGwsYSgpKSxkPXRoaXMuX2NhbmNlbEhhbmRsZXJzLmNvbmNhdCh0aGlzLm9uY2FuY2VsP1t0aGlzLm9uY2FuY2VsXTpbXSk7c2V0VGltZW91dChmdW5jdGlvbigpe2QuZm9yRWFjaChmdW5jdGlvbihhKXthLmNhbGwoYy50YXJnZXQsYyl9KX0sMCl9O3ZhciBoPWYuYWRkRXZlbnRMaXN0ZW5lcjtmLmFkZEV2ZW50TGlzdGVuZXI9ZnVuY3Rpb24oYSxiKXsiZnVuY3Rpb24iPT10eXBlb2YgYiYmImNhbmNlbCI9PWE/dGhpcy5fY2FuY2VsSGFuZGxlcnMucHVzaChiKTpoLmNhbGwodGhpcyxhLGIpfTt2YXIgaT1mLnJlbW92ZUV2ZW50TGlzdGVuZXI7cmV0dXJuIGYucmVtb3ZlRXZlbnRMaXN0ZW5lcj1mdW5jdGlvbihhLGIpe2lmKCJjYW5jZWwiPT1hKXt2YXIgYz10aGlzLl9jYW5jZWxIYW5kbGVycy5pbmRleE9mKGIpO2M+PTAmJnRoaXMuX2NhbmNlbEhhbmRsZXJzLnNwbGljZShjLDEpfWVsc2UgaS5jYWxsKHRoaXMsYSxiKX0sZn19fSgpLGZ1bmN0aW9uKGEpe3ZhciBiPWRvY3VtZW50LmRvY3VtZW50RWxlbWVudCxjPW51bGwsZD0hMTt0cnl7dmFyIGU9Z2V0Q29tcHV0ZWRTdHlsZShiKS5nZXRQcm9wZXJ0eVZhbHVlKCJvcGFjaXR5IiksZj0iMCI9PWU/IjEiOiIwIjtjPWIuYW5pbWF0ZSh7b3BhY2l0eTpbZixmXX0se2R1cmF0aW9uOjF9KSxjLmN1cnJlbnRUaW1lPTAsZD1nZXRDb21wdXRlZFN0eWxlKGIpLmdldFByb3BlcnR5VmFsdWUoIm9wYWNpdHkiKT09Zn1jYXRjaChnKXt9ZmluYWxseXtjJiZjLmNhbmNlbCgpfWlmKCFkKXt2YXIgaD13aW5kb3cuRWxlbWVudC5wcm90b3R5cGUuYW5pbWF0ZTt3aW5kb3cuRWxlbWVudC5wcm90b3R5cGUuYW5pbWF0ZT1mdW5jdGlvbihiLGMpe3JldHVybiB3aW5kb3cuU3ltYm9sJiZTeW1ib2wuaXRlcmF0b3ImJkFycmF5LnByb3RvdHlwZS5mcm9tJiZiW1N5bWJvbC5pdGVyYXRvcl0mJihiPUFycmF5LmZyb20oYikpLEFycmF5LmlzQXJyYXkoYil8fG51bGw9PT1ifHwoYj1hLmNvbnZlcnRUb0FycmF5Rm9ybShiKSksaC5jYWxsKHRoaXMsYixjKX19fShjKSwhZnVuY3Rpb24oYSxiLGMpe2Z1bmN0aW9uIGQoYSl7dmFyIGI9d2luZG93LmRvY3VtZW50LnRpbWVsaW5lO2IuY3VycmVudFRpbWU9YSxiLl9kaXNjYXJkQW5pbWF0aW9ucygpLDA9PWIuX2FuaW1hdGlvbnMubGVuZ3RoP2Y9ITE6cmVxdWVzdEFuaW1hdGlvbkZyYW1lKGQpOwp9dmFyIGU9d2luZG93LnJlcXVlc3RBbmltYXRpb25GcmFtZTt3aW5kb3cucmVxdWVzdEFuaW1hdGlvbkZyYW1lPWZ1bmN0aW9uKGEpe3JldHVybiBlKGZ1bmN0aW9uKGIpe3dpbmRvdy5kb2N1bWVudC50aW1lbGluZS5fdXBkYXRlQW5pbWF0aW9uc1Byb21pc2VzKCksYShiKSx3aW5kb3cuZG9jdW1lbnQudGltZWxpbmUuX3VwZGF0ZUFuaW1hdGlvbnNQcm9taXNlcygpfSl9LGIuQW5pbWF0aW9uVGltZWxpbmU9ZnVuY3Rpb24oKXt0aGlzLl9hbmltYXRpb25zPVtdLHRoaXMuY3VycmVudFRpbWU9dm9pZCAwfSxiLkFuaW1hdGlvblRpbWVsaW5lLnByb3RvdHlwZT17Z2V0QW5pbWF0aW9uczpmdW5jdGlvbigpe3JldHVybiB0aGlzLl9kaXNjYXJkQW5pbWF0aW9ucygpLHRoaXMuX2FuaW1hdGlvbnMuc2xpY2UoKX0sX3VwZGF0ZUFuaW1hdGlvbnNQcm9taXNlczpmdW5jdGlvbigpe2IuYW5pbWF0aW9uc1dpdGhQcm9taXNlcz1iLmFuaW1hdGlvbnNXaXRoUHJvbWlzZXMuZmlsdGVyKGZ1bmN0aW9uKGEpe3JldHVybiBhLl91cGRhdGVQcm9taXNlcygpfSl9LF9kaXNjYXJkQW5pbWF0aW9uczpmdW5jdGlvbigpe3RoaXMuX3VwZGF0ZUFuaW1hdGlvbnNQcm9taXNlcygpLHRoaXMuX2FuaW1hdGlvbnM9dGhpcy5fYW5pbWF0aW9ucy5maWx0ZXIoZnVuY3Rpb24oYSl7cmV0dXJuImZpbmlzaGVkIiE9YS5wbGF5U3RhdGUmJiJpZGxlIiE9YS5wbGF5U3RhdGV9KX0sX3BsYXk6ZnVuY3Rpb24oYSl7dmFyIGM9bmV3IGIuQW5pbWF0aW9uKGEsdGhpcyk7cmV0dXJuIHRoaXMuX2FuaW1hdGlvbnMucHVzaChjKSxiLnJlc3RhcnRXZWJBbmltYXRpb25zTmV4dFRpY2soKSxjLl91cGRhdGVQcm9taXNlcygpLGMuX2FuaW1hdGlvbi5wbGF5KCksYy5fdXBkYXRlUHJvbWlzZXMoKSxjfSxwbGF5OmZ1bmN0aW9uKGEpe3JldHVybiBhJiZhLnJlbW92ZSgpLHRoaXMuX3BsYXkoYSl9fTt2YXIgZj0hMTtiLnJlc3RhcnRXZWJBbmltYXRpb25zTmV4dFRpY2s9ZnVuY3Rpb24oKXtmfHwoZj0hMCxyZXF1ZXN0QW5pbWF0aW9uRnJhbWUoZCkpfTt2YXIgZz1uZXcgYi5BbmltYXRpb25UaW1lbGluZTtiLnRpbWVsaW5lPWc7dHJ5e09iamVjdC5kZWZpbmVQcm9wZXJ0eSh3aW5kb3cuZG9jdW1lbnQsInRpbWVsaW5lIix7Y29uZmlndXJhYmxlOiEwLGdldDpmdW5jdGlvbigpe3JldHVybiBnfX0pfWNhdGNoKGgpe310cnl7d2luZG93LmRvY3VtZW50LnRpbWVsaW5lPWd9Y2F0Y2goaCl7fX0oYyxlLGYpLGZ1bmN0aW9uKGEsYixjKXtiLmFuaW1hdGlvbnNXaXRoUHJvbWlzZXM9W10sYi5BbmltYXRpb249ZnVuY3Rpb24oYixjKXtpZih0aGlzLmlkPSIiLGImJmIuX2lkJiYodGhpcy5pZD1iLl9pZCksdGhpcy5lZmZlY3Q9YixiJiYoYi5fYW5pbWF0aW9uPXRoaXMpLCFjKXRocm93IG5ldyBFcnJvcigiQW5pbWF0aW9uIHdpdGggbnVsbCB0aW1lbGluZSBpcyBub3Qgc3VwcG9ydGVkIik7dGhpcy5fdGltZWxpbmU9Yyx0aGlzLl9zZXF1ZW5jZU51bWJlcj1hLnNlcXVlbmNlTnVtYmVyKyssdGhpcy5faG9sZFRpbWU9MCx0aGlzLl9wYXVzZWQ9ITEsdGhpcy5faXNHcm91cD0hMSx0aGlzLl9hbmltYXRpb249bnVsbCx0aGlzLl9jaGlsZEFuaW1hdGlvbnM9W10sdGhpcy5fY2FsbGJhY2s9bnVsbCx0aGlzLl9vbGRQbGF5U3RhdGU9ImlkbGUiLHRoaXMuX3JlYnVpbGRVbmRlcmx5aW5nQW5pbWF0aW9uKCksdGhpcy5fYW5pbWF0aW9uLmNhbmNlbCgpLHRoaXMuX3VwZGF0ZVByb21pc2VzKCl9LGIuQW5pbWF0aW9uLnByb3RvdHlwZT17X3VwZGF0ZVByb21pc2VzOmZ1bmN0aW9uKCl7dmFyIGE9dGhpcy5fb2xkUGxheVN0YXRlLGI9dGhpcy5wbGF5U3RhdGU7cmV0dXJuIHRoaXMuX3JlYWR5UHJvbWlzZSYmYiE9PWEmJigiaWRsZSI9PWI/KHRoaXMuX3JlamVjdFJlYWR5UHJvbWlzZSgpLHRoaXMuX3JlYWR5UHJvbWlzZT12b2lkIDApOiJwZW5kaW5nIj09YT90aGlzLl9yZXNvbHZlUmVhZHlQcm9taXNlKCk6InBlbmRpbmciPT1iJiYodGhpcy5fcmVhZHlQcm9taXNlPXZvaWQgMCkpLHRoaXMuX2ZpbmlzaGVkUHJvbWlzZSYmYiE9PWEmJigiaWRsZSI9PWI/KHRoaXMuX3JlamVjdEZpbmlzaGVkUHJvbWlzZSgpLHRoaXMuX2ZpbmlzaGVkUHJvbWlzZT12b2lkIDApOiJmaW5pc2hlZCI9PWI/dGhpcy5fcmVzb2x2ZUZpbmlzaGVkUHJvbWlzZSgpOiJmaW5pc2hlZCI9PWEmJih0aGlzLl9maW5pc2hlZFByb21pc2U9dm9pZCAwKSksdGhpcy5fb2xkUGxheVN0YXRlPXRoaXMucGxheVN0YXRlLHRoaXMuX3JlYWR5UHJvbWlzZXx8dGhpcy5fZmluaXNoZWRQcm9taXNlfSxfcmVidWlsZFVuZGVybHlpbmdBbmltYXRpb246ZnVuY3Rpb24oKXt0aGlzLl91cGRhdGVQcm9taXNlcygpO3ZhciBhLGMsZCxlLGY9ISF0aGlzLl9hbmltYXRpb247ZiYmKGE9dGhpcy5wbGF5YmFja1JhdGUsYz10aGlzLl9wYXVzZWQsZD10aGlzLnN0YXJ0VGltZSxlPXRoaXMuY3VycmVudFRpbWUsdGhpcy5fYW5pbWF0aW9uLmNhbmNlbCgpLHRoaXMuX2FuaW1hdGlvbi5fd3JhcHBlcj1udWxsLHRoaXMuX2FuaW1hdGlvbj1udWxsKSwoIXRoaXMuZWZmZWN0fHx0aGlzLmVmZmVjdCBpbnN0YW5jZW9mIHdpbmRvdy5LZXlmcmFtZUVmZmVjdCkmJih0aGlzLl9hbmltYXRpb249Yi5uZXdVbmRlcmx5aW5nQW5pbWF0aW9uRm9yS2V5ZnJhbWVFZmZlY3QodGhpcy5lZmZlY3QpLGIuYmluZEFuaW1hdGlvbkZvcktleWZyYW1lRWZmZWN0KHRoaXMpKSwodGhpcy5lZmZlY3QgaW5zdGFuY2VvZiB3aW5kb3cuU2VxdWVuY2VFZmZlY3R8fHRoaXMuZWZmZWN0IGluc3RhbmNlb2Ygd2luZG93Lkdyb3VwRWZmZWN0KSYmKHRoaXMuX2FuaW1hdGlvbj1iLm5ld1VuZGVybHlpbmdBbmltYXRpb25Gb3JHcm91cCh0aGlzLmVmZmVjdCksYi5iaW5kQW5pbWF0aW9uRm9yR3JvdXAodGhpcykpLHRoaXMuZWZmZWN0JiZ0aGlzLmVmZmVjdC5fb25zYW1wbGUmJmIuYmluZEFuaW1hdGlvbkZvckN1c3RvbUVmZmVjdCh0aGlzKSxmJiYoMSE9YSYmKHRoaXMucGxheWJhY2tSYXRlPWEpLG51bGwhPT1kP3RoaXMuc3RhcnRUaW1lPWQ6bnVsbCE9PWU/dGhpcy5jdXJyZW50VGltZT1lOm51bGwhPT10aGlzLl9ob2xkVGltZSYmKHRoaXMuY3VycmVudFRpbWU9dGhpcy5faG9sZFRpbWUpLGMmJnRoaXMucGF1c2UoKSksdGhpcy5fdXBkYXRlUHJvbWlzZXMoKX0sX3VwZGF0ZUNoaWxkcmVuOmZ1bmN0aW9uKCl7aWYodGhpcy5lZmZlY3QmJiJpZGxlIiE9dGhpcy5wbGF5U3RhdGUpe3ZhciBhPXRoaXMuZWZmZWN0Ll90aW1pbmcuZGVsYXk7dGhpcy5fY2hpbGRBbmltYXRpb25zLmZvckVhY2goZnVuY3Rpb24oYyl7dGhpcy5fYXJyYW5nZUNoaWxkcmVuKGMsYSksdGhpcy5lZmZlY3QgaW5zdGFuY2VvZiB3aW5kb3cuU2VxdWVuY2VFZmZlY3QmJihhKz1iLmdyb3VwQ2hpbGREdXJhdGlvbihjLmVmZmVjdCkpfS5iaW5kKHRoaXMpKX19LF9zZXRFeHRlcm5hbEFuaW1hdGlvbjpmdW5jdGlvbihhKXtpZih0aGlzLmVmZmVjdCYmdGhpcy5faXNHcm91cClmb3IodmFyIGI9MDtiPHRoaXMuZWZmZWN0LmNoaWxkcmVuLmxlbmd0aDtiKyspdGhpcy5lZmZlY3QuY2hpbGRyZW5bYl0uX2FuaW1hdGlvbj1hLHRoaXMuX2NoaWxkQW5pbWF0aW9uc1tiXS5fc2V0RXh0ZXJuYWxBbmltYXRpb24oYSl9LF9jb25zdHJ1Y3RDaGlsZEFuaW1hdGlvbnM6ZnVuY3Rpb24oKXtpZih0aGlzLmVmZmVjdCYmdGhpcy5faXNHcm91cCl7dmFyIGE9dGhpcy5lZmZlY3QuX3RpbWluZy5kZWxheTt0aGlzLl9yZW1vdmVDaGlsZEFuaW1hdGlvbnMoKSx0aGlzLmVmZmVjdC5jaGlsZHJlbi5mb3JFYWNoKGZ1bmN0aW9uKGMpe3ZhciBkPXdpbmRvdy5kb2N1bWVudC50aW1lbGluZS5fcGxheShjKTt0aGlzLl9jaGlsZEFuaW1hdGlvbnMucHVzaChkKSxkLnBsYXliYWNrUmF0ZT10aGlzLnBsYXliYWNrUmF0ZSx0aGlzLl9wYXVzZWQmJmQucGF1c2UoKSxjLl9hbmltYXRpb249dGhpcy5lZmZlY3QuX2FuaW1hdGlvbix0aGlzLl9hcnJhbmdlQ2hpbGRyZW4oZCxhKSx0aGlzLmVmZmVjdCBpbnN0YW5jZW9mIHdpbmRvdy5TZXF1ZW5jZUVmZmVjdCYmKGErPWIuZ3JvdXBDaGlsZER1cmF0aW9uKGMpKX0uYmluZCh0aGlzKSl9fSxfYXJyYW5nZUNoaWxkcmVuOmZ1bmN0aW9uKGEsYil7bnVsbD09PXRoaXMuc3RhcnRUaW1lP2EuY3VycmVudFRpbWU9dGhpcy5jdXJyZW50VGltZS1iL3RoaXMucGxheWJhY2tSYXRlOmEuc3RhcnRUaW1lIT09dGhpcy5zdGFydFRpbWUrYi90aGlzLnBsYXliYWNrUmF0ZSYmKGEuc3RhcnRUaW1lPXRoaXMuc3RhcnRUaW1lK2IvdGhpcy5wbGF5YmFja1JhdGUpfSxnZXQgdGltZWxpbmUoKXtyZXR1cm4gdGhpcy5fdGltZWxpbmV9LGdldCBwbGF5U3RhdGUoKXtyZXR1cm4gdGhpcy5fYW5pbWF0aW9uP3RoaXMuX2FuaW1hdGlvbi5wbGF5U3RhdGU6ImlkbGUifSxnZXQgZmluaXNoZWQoKXtyZXR1cm4gd2luZG93LlByb21pc2U/KHRoaXMuX2ZpbmlzaGVkUHJvbWlzZXx8KC0xPT1iLmFuaW1hdGlvbnNXaXRoUHJvbWlzZXMuaW5kZXhPZih0aGlzKSYmYi5hbmltYXRpb25zV2l0aFByb21pc2VzLnB1c2godGhpcyksdGhpcy5fZmluaXNoZWRQcm9taXNlPW5ldyBQcm9taXNlKGZ1bmN0aW9uKGEsYil7dGhpcy5fcmVzb2x2ZUZpbmlzaGVkUHJvbWlzZT1mdW5jdGlvbigpe2EodGhpcyl9LHRoaXMuX3JlamVjdEZpbmlzaGVkUHJvbWlzZT1mdW5jdGlvbigpe2Ioe3R5cGU6RE9NRXhjZXB0aW9uLkFCT1JUX0VSUixuYW1lOiJBYm9ydEVycm9yIn0pfX0uYmluZCh0aGlzKSksImZpbmlzaGVkIj09dGhpcy5wbGF5U3RhdGUmJnRoaXMuX3Jlc29sdmVGaW5pc2hlZFByb21pc2UoKSksdGhpcy5fZmluaXNoZWRQcm9taXNlKTooY29uc29sZS53YXJuKCJBbmltYXRpb24gUHJvbWlzZXMgcmVxdWlyZSBKYXZhU2NyaXB0IFByb21pc2UgY29uc3RydWN0b3IiKSxudWxsKX0sZ2V0IHJlYWR5KCl7cmV0dXJuIHdpbmRvdy5Qcm9taXNlPyh0aGlzLl9yZWFkeVByb21pc2V8fCgtMT09Yi5hbmltYXRpb25zV2l0aFByb21pc2VzLmluZGV4T2YodGhpcykmJmIuYW5pbWF0aW9uc1dpdGhQcm9taXNlcy5wdXNoKHRoaXMpLHRoaXMuX3JlYWR5UHJvbWlzZT1uZXcgUHJvbWlzZShmdW5jdGlvbihhLGIpe3RoaXMuX3Jlc29sdmVSZWFkeVByb21pc2U9ZnVuY3Rpb24oKXthKHRoaXMpfSx0aGlzLl9yZWplY3RSZWFkeVByb21pc2U9ZnVuY3Rpb24oKXtiKHt0eXBlOkRPTUV4Y2VwdGlvbi5BQk9SVF9FUlIsbmFtZToiQWJvcnRFcnJvciJ9KX19LmJpbmQodGhpcykpLCJwZW5kaW5nIiE9PXRoaXMucGxheVN0YXRlJiZ0aGlzLl9yZXNvbHZlUmVhZHlQcm9taXNlKCkpLHRoaXMuX3JlYWR5UHJvbWlzZSk6KGNvbnNvbGUud2FybigiQW5pbWF0aW9uIFByb21pc2VzIHJlcXVpcmUgSmF2YVNjcmlwdCBQcm9taXNlIGNvbnN0cnVjdG9yIiksbnVsbCl9LGdldCBvbmZpbmlzaCgpe3JldHVybiB0aGlzLl9hbmltYXRpb24ub25maW5pc2h9LHNldCBvbmZpbmlzaChhKXsiZnVuY3Rpb24iPT10eXBlb2YgYT90aGlzLl9hbmltYXRpb24ub25maW5pc2g9ZnVuY3Rpb24oYil7Yi50YXJnZXQ9dGhpcyxhLmNhbGwodGhpcyxiKX0uYmluZCh0aGlzKTp0aGlzLl9hbmltYXRpb24ub25maW5pc2g9YX0sZ2V0IG9uY2FuY2VsKCl7cmV0dXJuIHRoaXMuX2FuaW1hdGlvbi5vbmNhbmNlbH0sc2V0IG9uY2FuY2VsKGEpeyJmdW5jdGlvbiI9PXR5cGVvZiBhP3RoaXMuX2FuaW1hdGlvbi5vbmNhbmNlbD1mdW5jdGlvbihiKXtiLnRhcmdldD10aGlzLGEuY2FsbCh0aGlzLGIpfS5iaW5kKHRoaXMpOnRoaXMuX2FuaW1hdGlvbi5vbmNhbmNlbD1hfSxnZXQgY3VycmVudFRpbWUoKXt0aGlzLl91cGRhdGVQcm9taXNlcygpO3ZhciBhPXRoaXMuX2FuaW1hdGlvbi5jdXJyZW50VGltZTtyZXR1cm4gdGhpcy5fdXBkYXRlUHJvbWlzZXMoKSxhfSxzZXQgY3VycmVudFRpbWUoYSl7dGhpcy5fdXBkYXRlUHJvbWlzZXMoKSx0aGlzLl9hbmltYXRpb24uY3VycmVudFRpbWU9aXNGaW5pdGUoYSk/YTpNYXRoLnNpZ24oYSkqTnVtYmVyLk1BWF9WQUxVRSx0aGlzLl9yZWdpc3RlcigpLHRoaXMuX2ZvckVhY2hDaGlsZChmdW5jdGlvbihiLGMpe2IuY3VycmVudFRpbWU9YS1jfSksdGhpcy5fdXBkYXRlUHJvbWlzZXMoKX0sZ2V0IHN0YXJ0VGltZSgpe3JldHVybiB0aGlzLl9hbmltYXRpb24uc3RhcnRUaW1lfSxzZXQgc3RhcnRUaW1lKGEpe3RoaXMuX3VwZGF0ZVByb21pc2VzKCksdGhpcy5fYW5pbWF0aW9uLnN0YXJ0VGltZT1pc0Zpbml0ZShhKT9hOk1hdGguc2lnbihhKSpOdW1iZXIuTUFYX1ZBTFVFLHRoaXMuX3JlZ2lzdGVyKCksdGhpcy5fZm9yRWFjaENoaWxkKGZ1bmN0aW9uKGIsYyl7Yi5zdGFydFRpbWU9YStjfSksdGhpcy5fdXBkYXRlUHJvbWlzZXMoKX0sZ2V0IHBsYXliYWNrUmF0ZSgpe3JldHVybiB0aGlzLl9hbmltYXRpb24ucGxheWJhY2tSYXRlfSxzZXQgcGxheWJhY2tSYXRlKGEpe3RoaXMuX3VwZGF0ZVByb21pc2VzKCk7dmFyIGI9dGhpcy5jdXJyZW50VGltZTt0aGlzLl9hbmltYXRpb24ucGxheWJhY2tSYXRlPWEsdGhpcy5fZm9yRWFjaENoaWxkKGZ1bmN0aW9uKGIpe2IucGxheWJhY2tSYXRlPWF9KSwicGF1c2VkIiE9dGhpcy5wbGF5U3RhdGUmJiJpZGxlIiE9dGhpcy5wbGF5U3RhdGUmJnRoaXMucGxheSgpLG51bGwhPT1iJiYodGhpcy5jdXJyZW50VGltZT1iKSx0aGlzLl91cGRhdGVQcm9taXNlcygpfSxwbGF5OmZ1bmN0aW9uKCl7dGhpcy5fdXBkYXRlUHJvbWlzZXMoKSx0aGlzLl9wYXVzZWQ9ITEsdGhpcy5fYW5pbWF0aW9uLnBsYXkoKSwtMT09dGhpcy5fdGltZWxpbmUuX2FuaW1hdGlvbnMuaW5kZXhPZih0aGlzKSYmdGhpcy5fdGltZWxpbmUuX2FuaW1hdGlvbnMucHVzaCh0aGlzKSx0aGlzLl9yZWdpc3RlcigpLGIuYXdhaXRTdGFydFRpbWUodGhpcyksdGhpcy5fZm9yRWFjaENoaWxkKGZ1bmN0aW9uKGEpe3ZhciBiPWEuY3VycmVudFRpbWU7YS5wbGF5KCksYS5jdXJyZW50VGltZT1ifSksdGhpcy5fdXBkYXRlUHJvbWlzZXMoKX0scGF1c2U6ZnVuY3Rpb24oKXt0aGlzLl91cGRhdGVQcm9taXNlcygpLHRoaXMuY3VycmVudFRpbWUmJih0aGlzLl9ob2xkVGltZT10aGlzLmN1cnJlbnRUaW1lKSx0aGlzLl9hbmltYXRpb24ucGF1c2UoKSx0aGlzLl9yZWdpc3RlcigpLHRoaXMuX2ZvckVhY2hDaGlsZChmdW5jdGlvbihhKXthLnBhdXNlKCl9KSx0aGlzLl9wYXVzZWQ9ITAsdGhpcy5fdXBkYXRlUHJvbWlzZXMoKX0sZmluaXNoOmZ1bmN0aW9uKCl7dGhpcy5fdXBkYXRlUHJvbWlzZXMoKSx0aGlzLl9hbmltYXRpb24uZmluaXNoKCksdGhpcy5fcmVnaXN0ZXIoKSx0aGlzLl91cGRhdGVQcm9taXNlcygpfSxjYW5jZWw6ZnVuY3Rpb24oKXt0aGlzLl91cGRhdGVQcm9taXNlcygpLHRoaXMuX2FuaW1hdGlvbi5jYW5jZWwoKSx0aGlzLl9yZWdpc3RlcigpLHRoaXMuX3JlbW92ZUNoaWxkQW5pbWF0aW9ucygpLHRoaXMuX3VwZGF0ZVByb21pc2VzKCl9LHJldmVyc2U6ZnVuY3Rpb24oKXt0aGlzLl91cGRhdGVQcm9taXNlcygpO3ZhciBhPXRoaXMuY3VycmVudFRpbWU7dGhpcy5fYW5pbWF0aW9uLnJldmVyc2UoKSx0aGlzLl9mb3JFYWNoQ2hpbGQoZnVuY3Rpb24oYSl7YS5yZXZlcnNlKCl9KSxudWxsIT09YSYmKHRoaXMuY3VycmVudFRpbWU9YSksdGhpcy5fdXBkYXRlUHJvbWlzZXMoKX0sYWRkRXZlbnRMaXN0ZW5lcjpmdW5jdGlvbihhLGIpe3ZhciBjPWI7ImZ1bmN0aW9uIj09dHlwZW9mIGImJihjPWZ1bmN0aW9uKGEpe2EudGFyZ2V0PXRoaXMsYi5jYWxsKHRoaXMsYSl9LmJpbmQodGhpcyksYi5fd3JhcHBlcj1jKSx0aGlzLl9hbmltYXRpb24uYWRkRXZlbnRMaXN0ZW5lcihhLGMpfSxyZW1vdmVFdmVudExpc3RlbmVyOmZ1bmN0aW9uKGEsYil7dGhpcy5fYW5pbWF0aW9uLnJlbW92ZUV2ZW50TGlzdGVuZXIoYSxiJiZiLl93cmFwcGVyfHxiKX0sX3JlbW92ZUNoaWxkQW5pbWF0aW9uczpmdW5jdGlvbigpe2Zvcig7dGhpcy5fY2hpbGRBbmltYXRpb25zLmxlbmd0aDspdGhpcy5fY2hpbGRBbmltYXRpb25zLnBvcCgpLmNhbmNlbCgpfSxfZm9yRWFjaENoaWxkOmZ1bmN0aW9uKGIpe3ZhciBjPTA7aWYodGhpcy5lZmZlY3QuY2hpbGRyZW4mJnRoaXMuX2NoaWxkQW5pbWF0aW9ucy5sZW5ndGg8dGhpcy5lZmZlY3QuY2hpbGRyZW4ubGVuZ3RoJiZ0aGlzLl9jb25zdHJ1Y3RDaGlsZEFuaW1hdGlvbnMoKSx0aGlzLl9jaGlsZEFuaW1hdGlvbnMuZm9yRWFjaChmdW5jdGlvbihhKXtiLmNhbGwodGhpcyxhLGMpLHRoaXMuZWZmZWN0IGluc3RhbmNlb2Ygd2luZG93LlNlcXVlbmNlRWZmZWN0JiYoYys9YS5lZmZlY3QuYWN0aXZlRHVyYXRpb24pfS5iaW5kKHRoaXMpKSwicGVuZGluZyIhPXRoaXMucGxheVN0YXRlKXt2YXIgZD10aGlzLmVmZmVjdC5fdGltaW5nLGU9dGhpcy5jdXJyZW50VGltZTtudWxsIT09ZSYmKGU9YS5jYWxjdWxhdGVUaW1lRnJhY3Rpb24oYS5jYWxjdWxhdGVBY3RpdmVEdXJhdGlvbihkKSxlLGQpKSwobnVsbD09ZXx8aXNOYU4oZSkpJiZ0aGlzLl9yZW1vdmVDaGlsZEFuaW1hdGlvbnMoKX19fSx3aW5kb3cuQW5pbWF0aW9uPWIuQW5pbWF0aW9ufShjLGUsZiksZnVuY3Rpb24oYSxiLGMpe2Z1bmN0aW9uIGQoYil7dGhpcy5fZnJhbWVzPWEubm9ybWFsaXplS2V5ZnJhbWVzKGIpfWZ1bmN0aW9uIGUoKXtmb3IodmFyIGE9ITE7aS5sZW5ndGg7KXt2YXIgYj1pLnNoaWZ0KCk7Yi5fdXBkYXRlQ2hpbGRyZW4oKSxhPSEwfXJldHVybiBhfXZhciBmPWZ1bmN0aW9uKGEpe2lmKGEuX2FuaW1hdGlvbj12b2lkIDAsYSBpbnN0YW5jZW9mIHdpbmRvdy5TZXF1ZW5jZUVmZmVjdHx8YSBpbnN0YW5jZW9mIHdpbmRvdy5Hcm91cEVmZmVjdClmb3IodmFyIGI9MDtiPGEuY2hpbGRyZW4ubGVuZ3RoO2IrKylmKGEuY2hpbGRyZW5bYl0pfTtiLnJlbW92ZU11bHRpPWZ1bmN0aW9uKGEpe2Zvcih2YXIgYj1bXSxjPTA7YzxhLmxlbmd0aDtjKyspe3ZhciBkPWFbY107ZC5fcGFyZW50PygtMT09Yi5pbmRleE9mKGQuX3BhcmVudCkmJmIucHVzaChkLl9wYXJlbnQpLGQuX3BhcmVudC5jaGlsZHJlbi5zcGxpY2UoZC5fcGFyZW50LmNoaWxkcmVuLmluZGV4T2YoZCksMSksZC5fcGFyZW50PW51bGwsZihkKSk6ZC5fYW5pbWF0aW9uJiZkLl9hbmltYXRpb24uZWZmZWN0PT1kJiYoZC5fYW5pbWF0aW9uLmNhbmNlbCgpLGQuX2FuaW1hdGlvbi5lZmZlY3Q9bmV3IEtleWZyYW1lRWZmZWN0KG51bGwsW10pLGQuX2FuaW1hdGlvbi5fY2FsbGJhY2smJihkLl9hbmltYXRpb24uX2NhbGxiYWNrLl9hbmltYXRpb249bnVsbCksZC5fYW5pbWF0aW9uLl9yZWJ1aWxkVW5kZXJseWluZ0FuaW1hdGlvbigpLGYoZCkpfWZvcihjPTA7YzxiLmxlbmd0aDtjKyspYltjXS5fcmVidWlsZCgpfSxiLktleWZyYW1lRWZmZWN0PWZ1bmN0aW9uKGIsYyxlLGYpe3JldHVybiB0aGlzLnRhcmdldD1iLHRoaXMuX3BhcmVudD1udWxsLGU9YS5udW1lcmljVGltaW5nVG9PYmplY3QoZSksdGhpcy5fdGltaW5nSW5wdXQ9YS5jbG9uZVRpbWluZ0lucHV0KGUpLHRoaXMuX3RpbWluZz1hLm5vcm1hbGl6ZVRpbWluZ0lucHV0KGUpLHRoaXMudGltaW5nPWEubWFrZVRpbWluZyhlLCExLHRoaXMpLHRoaXMudGltaW5nLl9lZmZlY3Q9dGhpcywiZnVuY3Rpb24iPT10eXBlb2YgYz8oYS5kZXByZWNhdGVkKCJDdXN0b20gS2V5ZnJhbWVFZmZlY3QiLCIyMDE1LTA2LTIyIiwiVXNlIEtleWZyYW1lRWZmZWN0Lm9uc2FtcGxlIGluc3RlYWQuIiksdGhpcy5fbm9ybWFsaXplZEtleWZyYW1lcz1jKTp0aGlzLl9ub3JtYWxpemVkS2V5ZnJhbWVzPW5ldyBkKGMpLHRoaXMuX2tleWZyYW1lcz1jLHRoaXMuYWN0aXZlRHVyYXRpb249YS5jYWxjdWxhdGVBY3RpdmVEdXJhdGlvbih0aGlzLl90aW1pbmcpLHRoaXMuX2lkPWYsdGhpc30sYi5LZXlmcmFtZUVmZmVjdC5wcm90b3R5cGU9e2dldEZyYW1lczpmdW5jdGlvbigpe3JldHVybiJmdW5jdGlvbiI9PXR5cGVvZiB0aGlzLl9ub3JtYWxpemVkS2V5ZnJhbWVzP3RoaXMuX25vcm1hbGl6ZWRLZXlmcmFtZXM6dGhpcy5fbm9ybWFsaXplZEtleWZyYW1lcy5fZnJhbWVzfSxzZXQgb25zYW1wbGUoYSl7aWYoImZ1bmN0aW9uIj09dHlwZW9mIHRoaXMuZ2V0RnJhbWVzKCkpdGhyb3cgbmV3IEVycm9yKCJTZXR0aW5nIG9uc2FtcGxlIG9uIGN1c3RvbSBlZmZlY3QgS2V5ZnJhbWVFZmZlY3QgaXMgbm90IHN1cHBvcnRlZC4iKTt0aGlzLl9vbnNhbXBsZT1hLHRoaXMuX2FuaW1hdGlvbiYmdGhpcy5fYW5pbWF0aW9uLl9yZWJ1aWxkVW5kZXJseWluZ0FuaW1hdGlvbigpfSxnZXQgcGFyZW50KCl7cmV0dXJuIHRoaXMuX3BhcmVudH0sY2xvbmU6ZnVuY3Rpb24oKXtpZigiZnVuY3Rpb24iPT10eXBlb2YgdGhpcy5nZXRGcmFtZXMoKSl0aHJvdyBuZXcgRXJyb3IoIkNsb25pbmcgY3VzdG9tIGVmZmVjdHMgaXMgbm90IHN1cHBvcnRlZC4iKTt2YXIgYj1uZXcgS2V5ZnJhbWVFZmZlY3QodGhpcy50YXJnZXQsW10sYS5jbG9uZVRpbWluZ0lucHV0KHRoaXMuX3RpbWluZ0lucHV0KSx0aGlzLl9pZCk7cmV0dXJuIGIuX25vcm1hbGl6ZWRLZXlmcmFtZXM9dGhpcy5fbm9ybWFsaXplZEtleWZyYW1lcyxiLl9rZXlmcmFtZXM9dGhpcy5fa2V5ZnJhbWVzLGJ9LHJlbW92ZTpmdW5jdGlvbigpe2IucmVtb3ZlTXVsdGkoW3RoaXNdKX19O3ZhciBnPUVsZW1lbnQucHJvdG90eXBlLmFuaW1hdGU7RWxlbWVudC5wcm90b3R5cGUuYW5pbWF0ZT1mdW5jdGlvbihhLGMpe3ZhciBkPSIiO3JldHVybiBjJiZjLmlkJiYoZD1jLmlkKSxiLnRpbWVsaW5lLl9wbGF5KG5ldyBiLktleWZyYW1lRWZmZWN0KHRoaXMsYSxjLGQpKX07dmFyIGg9ZG9jdW1lbnQuY3JlYXRlRWxlbWVudE5TKCJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hodG1sIiwiZGl2Iik7Yi5uZXdVbmRlcmx5aW5nQW5pbWF0aW9uRm9yS2V5ZnJhbWVFZmZlY3Q9ZnVuY3Rpb24oYSl7aWYoYSl7dmFyIGI9YS50YXJnZXR8fGgsYz1hLl9rZXlmcmFtZXM7ImZ1bmN0aW9uIj09dHlwZW9mIGMmJihjPVtdKTt2YXIgZD1hLl90aW1pbmdJbnB1dDtkLmlkPWEuX2lkfWVsc2UgdmFyIGI9aCxjPVtdLGQ9MDtyZXR1cm4gZy5hcHBseShiLFtjLGRdKX0sYi5iaW5kQW5pbWF0aW9uRm9yS2V5ZnJhbWVFZmZlY3Q9ZnVuY3Rpb24oYSl7YS5lZmZlY3QmJiJmdW5jdGlvbiI9PXR5cGVvZiBhLmVmZmVjdC5fbm9ybWFsaXplZEtleWZyYW1lcyYmYi5iaW5kQW5pbWF0aW9uRm9yQ3VzdG9tRWZmZWN0KGEpfTt2YXIgaT1bXTtiLmF3YWl0U3RhcnRUaW1lPWZ1bmN0aW9uKGEpe251bGw9PT1hLnN0YXJ0VGltZSYmYS5faXNHcm91cCYmKDA9PWkubGVuZ3RoJiZyZXF1ZXN0QW5pbWF0aW9uRnJhbWUoZSksaS5wdXNoKGEpKX07dmFyIGo9d2luZG93LmdldENvbXB1dGVkU3R5bGU7T2JqZWN0LmRlZmluZVByb3BlcnR5KHdpbmRvdywiZ2V0Q29tcHV0ZWRTdHlsZSIse2NvbmZpZ3VyYWJsZTohMCxlbnVtZXJhYmxlOiEwLHZhbHVlOmZ1bmN0aW9uKCl7d2luZG93LmRvY3VtZW50LnRpbWVsaW5lLl91cGRhdGVBbmltYXRpb25zUHJvbWlzZXMoKTt2YXIgYT1qLmFwcGx5KHRoaXMsYXJndW1lbnRzKTtyZXR1cm4gZSgpJiYoYT1qLmFwcGx5KHRoaXMsYXJndW1lbnRzKSksd2luZG93LmRvY3VtZW50LnRpbWVsaW5lLl91cGRhdGVBbmltYXRpb25zUHJvbWlzZXMoKSxhfX0pLHdpbmRvdy5LZXlmcmFtZUVmZmVjdD1iLktleWZyYW1lRWZmZWN0LHdpbmRvdy5FbGVtZW50LnByb3RvdHlwZS5nZXRBbmltYXRpb25zPWZ1bmN0aW9uKCl7cmV0dXJuIGRvY3VtZW50LnRpbWVsaW5lLmdldEFuaW1hdGlvbnMoKS5maWx0ZXIoZnVuY3Rpb24oYSl7cmV0dXJuIG51bGwhPT1hLmVmZmVjdCYmYS5lZmZlY3QudGFyZ2V0PT10aGlzfS5iaW5kKHRoaXMpKX19KGMsZSxmKSxmdW5jdGlvbihhLGIsYyl7ZnVuY3Rpb24gZChhKXthLl9yZWdpc3RlcmVkfHwoYS5fcmVnaXN0ZXJlZD0hMCxnLnB1c2goYSksaHx8KGg9ITAscmVxdWVzdEFuaW1hdGlvbkZyYW1lKGUpKSl9ZnVuY3Rpb24gZShhKXt2YXIgYj1nO2c9W10sYi5zb3J0KGZ1bmN0aW9uKGEsYil7cmV0dXJuIGEuX3NlcXVlbmNlTnVtYmVyLWIuX3NlcXVlbmNlTnVtYmVyfSksYj1iLmZpbHRlcihmdW5jdGlvbihhKXthKCk7dmFyIGI9YS5fYW5pbWF0aW9uP2EuX2FuaW1hdGlvbi5wbGF5U3RhdGU6ImlkbGUiO3JldHVybiJydW5uaW5nIiE9YiYmInBlbmRpbmciIT1iJiYoYS5fcmVnaXN0ZXJlZD0hMSksYS5fcmVnaXN0ZXJlZH0pLGcucHVzaC5hcHBseShnLGIpLGcubGVuZ3RoPyhoPSEwLHJlcXVlc3RBbmltYXRpb25GcmFtZShlKSk6aD0hMX12YXIgZj0oZG9jdW1lbnQuY3JlYXRlRWxlbWVudE5TKCJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hodG1sIiwiZGl2IiksMCk7Yi5iaW5kQW5pbWF0aW9uRm9yQ3VzdG9tRWZmZWN0PWZ1bmN0aW9uKGIpe3ZhciBjLGU9Yi5lZmZlY3QudGFyZ2V0LGc9ImZ1bmN0aW9uIj09dHlwZW9mIGIuZWZmZWN0LmdldEZyYW1lcygpO2M9Zz9iLmVmZmVjdC5nZXRGcmFtZXMoKTpiLmVmZmVjdC5fb25zYW1wbGU7dmFyIGg9Yi5lZmZlY3QudGltaW5nLGk9bnVsbDtoPWEubm9ybWFsaXplVGltaW5nSW5wdXQoaCk7dmFyIGo9ZnVuY3Rpb24oKXt2YXIgZD1qLl9hbmltYXRpb24/ai5fYW5pbWF0aW9uLmN1cnJlbnRUaW1lOm51bGw7bnVsbCE9PWQmJihkPWEuY2FsY3VsYXRlVGltZUZyYWN0aW9uKGEuY2FsY3VsYXRlQWN0aXZlRHVyYXRpb24oaCksZCxoKSxpc05hTihkKSYmKGQ9bnVsbCkpLGQhPT1pJiYoZz9jKGQsZSxiLmVmZmVjdCk6YyhkLGIuZWZmZWN0LGIuZWZmZWN0Ll9hbmltYXRpb24pKSxpPWR9O2ouX2FuaW1hdGlvbj1iLGouX3JlZ2lzdGVyZWQ9ITEsai5fc2VxdWVuY2VOdW1iZXI9ZisrLGIuX2NhbGxiYWNrPWosZChqKX07dmFyIGc9W10saD0hMTtiLkFuaW1hdGlvbi5wcm90b3R5cGUuX3JlZ2lzdGVyPWZ1bmN0aW9uKCl7dGhpcy5fY2FsbGJhY2smJmQodGhpcy5fY2FsbGJhY2spfX0oYyxlLGYpLGZ1bmN0aW9uKGEsYixjKXtmdW5jdGlvbiBkKGEpe3JldHVybiBhLl90aW1pbmcuZGVsYXkrYS5hY3RpdmVEdXJhdGlvbithLl90aW1pbmcuZW5kRGVsYXl9ZnVuY3Rpb24gZShiLGMsZCl7dGhpcy5faWQ9ZCx0aGlzLl9wYXJlbnQ9bnVsbCx0aGlzLmNoaWxkcmVuPWJ8fFtdLHRoaXMuX3JlcGFyZW50KHRoaXMuY2hpbGRyZW4pLGM9YS5udW1lcmljVGltaW5nVG9PYmplY3QoYyksdGhpcy5fdGltaW5nSW5wdXQ9YS5jbG9uZVRpbWluZ0lucHV0KGMpLHRoaXMuX3RpbWluZz1hLm5vcm1hbGl6ZVRpbWluZ0lucHV0KGMsITApLHRoaXMudGltaW5nPWEubWFrZVRpbWluZyhjLCEwLHRoaXMpLHRoaXMudGltaW5nLl9lZmZlY3Q9dGhpcywiYXV0byI9PT10aGlzLl90aW1pbmcuZHVyYXRpb24mJih0aGlzLl90aW1pbmcuZHVyYXRpb249dGhpcy5hY3RpdmVEdXJhdGlvbil9d2luZG93LlNlcXVlbmNlRWZmZWN0PWZ1bmN0aW9uKCl7ZS5hcHBseSh0aGlzLGFyZ3VtZW50cyl9LHdpbmRvdy5Hcm91cEVmZmVjdD1mdW5jdGlvbigpe2UuYXBwbHkodGhpcyxhcmd1bWVudHMpfSxlLnByb3RvdHlwZT17X2lzQW5jZXN0b3I6ZnVuY3Rpb24oYSl7Zm9yKHZhciBiPXRoaXM7bnVsbCE9PWI7KXtpZihiPT1hKXJldHVybiEwO2I9Yi5fcGFyZW50fXJldHVybiExfSxfcmVidWlsZDpmdW5jdGlvbigpe2Zvcih2YXIgYT10aGlzO2E7KSJhdXRvIj09PWEudGltaW5nLmR1cmF0aW9uJiYoYS5fdGltaW5nLmR1cmF0aW9uPWEuYWN0aXZlRHVyYXRpb24pLGE9YS5fcGFyZW50O3RoaXMuX2FuaW1hdGlvbiYmdGhpcy5fYW5pbWF0aW9uLl9yZWJ1aWxkVW5kZXJseWluZ0FuaW1hdGlvbigpfSxfcmVwYXJlbnQ6ZnVuY3Rpb24oYSl7Yi5yZW1vdmVNdWx0aShhKTtmb3IodmFyIGM9MDtjPGEubGVuZ3RoO2MrKylhW2NdLl9wYXJlbnQ9dGhpc30sX3B1dENoaWxkOmZ1bmN0aW9uKGEsYil7Zm9yKHZhciBjPWI/IkNhbm5vdCBhcHBlbmQgYW4gYW5jZXN0b3Igb3Igc2VsZiI6IkNhbm5vdCBwcmVwZW5kIGFuIGFuY2VzdG9yIG9yIHNlbGYiLGQ9MDtkPGEubGVuZ3RoO2QrKylpZih0aGlzLl9pc0FuY2VzdG9yKGFbZF0pKXRocm93e3R5cGU6RE9NRXhjZXB0aW9uLkhJRVJBUkNIWV9SRVFVRVNUX0VSUixuYW1lOiJIaWVyYXJjaHlSZXF1ZXN0RXJyb3IiLG1lc3NhZ2U6Y307Zm9yKHZhciBkPTA7ZDxhLmxlbmd0aDtkKyspYj90aGlzLmNoaWxkcmVuLnB1c2goYVtkXSk6dGhpcy5jaGlsZHJlbi51bnNoaWZ0KGFbZF0pO3RoaXMuX3JlcGFyZW50KGEpLHRoaXMuX3JlYnVpbGQoKX0sYXBwZW5kOmZ1bmN0aW9uKCl7dGhpcy5fcHV0Q2hpbGQoYXJndW1lbnRzLCEwKX0scHJlcGVuZDpmdW5jdGlvbigpe3RoaXMuX3B1dENoaWxkKGFyZ3VtZW50cywhMSl9LGdldCBwYXJlbnQoKXtyZXR1cm4gdGhpcy5fcGFyZW50fSxnZXQgZmlyc3RDaGlsZCgpe3JldHVybiB0aGlzLmNoaWxkcmVuLmxlbmd0aD90aGlzLmNoaWxkcmVuWzBdOm51bGx9LGdldCBsYXN0Q2hpbGQoKXtyZXR1cm4gdGhpcy5jaGlsZHJlbi5sZW5ndGg/dGhpcy5jaGlsZHJlblt0aGlzLmNoaWxkcmVuLmxlbmd0aC0xXTpudWxsfSxjbG9uZTpmdW5jdGlvbigpe2Zvcih2YXIgYj1hLmNsb25lVGltaW5nSW5wdXQodGhpcy5fdGltaW5nSW5wdXQpLGM9W10sZD0wO2Q8dGhpcy5jaGlsZHJlbi5sZW5ndGg7ZCsrKWMucHVzaCh0aGlzLmNoaWxkcmVuW2RdLmNsb25lKCkpO3JldHVybiB0aGlzIGluc3RhbmNlb2YgR3JvdXBFZmZlY3Q/bmV3IEdyb3VwRWZmZWN0KGMsYik6bmV3IFNlcXVlbmNlRWZmZWN0KGMsYil9LHJlbW92ZTpmdW5jdGlvbigpe2IucmVtb3ZlTXVsdGkoW3RoaXNdKX19LHdpbmRvdy5TZXF1ZW5jZUVmZmVjdC5wcm90b3R5cGU9T2JqZWN0LmNyZWF0ZShlLnByb3RvdHlwZSksT2JqZWN0LmRlZmluZVByb3BlcnR5KHdpbmRvdy5TZXF1ZW5jZUVmZmVjdC5wcm90b3R5cGUsImFjdGl2ZUR1cmF0aW9uIix7Z2V0OmZ1bmN0aW9uKCl7dmFyIGE9MDtyZXR1cm4gdGhpcy5jaGlsZHJlbi5mb3JFYWNoKGZ1bmN0aW9uKGIpe2ErPWQoYil9KSxNYXRoLm1heChhLDApfX0pLHdpbmRvdy5Hcm91cEVmZmVjdC5wcm90b3R5cGU9T2JqZWN0LmNyZWF0ZShlLnByb3RvdHlwZSksT2JqZWN0LmRlZmluZVByb3BlcnR5KHdpbmRvdy5Hcm91cEVmZmVjdC5wcm90b3R5cGUsImFjdGl2ZUR1cmF0aW9uIix7Z2V0OmZ1bmN0aW9uKCl7dmFyIGE9MDtyZXR1cm4gdGhpcy5jaGlsZHJlbi5mb3JFYWNoKGZ1bmN0aW9uKGIpe2E9TWF0aC5tYXgoYSxkKGIpKX0pLGF9fSksYi5uZXdVbmRlcmx5aW5nQW5pbWF0aW9uRm9yR3JvdXA9ZnVuY3Rpb24oYyl7dmFyIGQsZT1udWxsLGY9ZnVuY3Rpb24oYil7dmFyIGM9ZC5fd3JhcHBlcjtyZXR1cm4gYyYmInBlbmRpbmciIT1jLnBsYXlTdGF0ZSYmYy5lZmZlY3Q/bnVsbD09Yj92b2lkIGMuX3JlbW92ZUNoaWxkQW5pbWF0aW9ucygpOjA9PWImJmMucGxheWJhY2tSYXRlPDAmJihlfHwoZT1hLm5vcm1hbGl6ZVRpbWluZ0lucHV0KGMuZWZmZWN0LnRpbWluZykpLGI9YS5jYWxjdWxhdGVUaW1lRnJhY3Rpb24oYS5jYWxjdWxhdGVBY3RpdmVEdXJhdGlvbihlKSwtMSxlKSxpc05hTihiKXx8bnVsbD09Yik/KGMuX2ZvckVhY2hDaGlsZChmdW5jdGlvbihhKXthLmN1cnJlbnRUaW1lPS0xfSksdm9pZCBjLl9yZW1vdmVDaGlsZEFuaW1hdGlvbnMoKSk6dm9pZCAwOnZvaWQgMH0sZz1uZXcgS2V5ZnJhbWVFZmZlY3QobnVsbCxbXSxjLl90aW1pbmcsYy5faWQpO3JldHVybiBnLm9uc2FtcGxlPWYsZD1iLnRpbWVsaW5lLl9wbGF5KGcpfSxiLmJpbmRBbmltYXRpb25Gb3JHcm91cD1mdW5jdGlvbihhKXthLl9hbmltYXRpb24uX3dyYXBwZXI9YSxhLl9pc0dyb3VwPSEwLGIuYXdhaXRTdGFydFRpbWUoYSksYS5fY29uc3RydWN0Q2hpbGRBbmltYXRpb25zKCksYS5fc2V0RXh0ZXJuYWxBbmltYXRpb24oYSl9LGIuZ3JvdXBDaGlsZER1cmF0aW9uPWR9KGMsZSxmKSxiWyJ0cnVlIl09YX0oe30sZnVuY3Rpb24oKXtyZXR1cm4gdGhpc30oKSk7Cjwvc2NyaXB0PjxzY3JpcHQ+LyoqCkBsaWNlbnNlIEBub2NvbXBpbGUKQ29weXJpZ2h0IChjKSAyMDE4IFRoZSBQb2x5bWVyIFByb2plY3QgQXV0aG9ycy4gQWxsIHJpZ2h0cyByZXNlcnZlZC4KVGhpcyBjb2RlIG1heSBvbmx5IGJlIHVzZWQgdW5kZXIgdGhlIEJTRCBzdHlsZSBsaWNlbnNlIGZvdW5kIGF0IGh0dHA6Ly9wb2x5bWVyLmdpdGh1Yi5pby9MSUNFTlNFLnR4dApUaGUgY29tcGxldGUgc2V0IG9mIGF1dGhvcnMgbWF5IGJlIGZvdW5kIGF0IGh0dHA6Ly9wb2x5bWVyLmdpdGh1Yi5pby9BVVRIT1JTLnR4dApUaGUgY29tcGxldGUgc2V0IG9mIGNvbnRyaWJ1dG9ycyBtYXkgYmUgZm91bmQgYXQgaHR0cDovL3BvbHltZXIuZ2l0aHViLmlvL0NPTlRSSUJVVE9SUy50eHQKQ29kZSBkaXN0cmlidXRlZCBieSBHb29nbGUgYXMgcGFydCBvZiB0aGUgcG9seW1lciBwcm9qZWN0IGlzIGFsc28Kc3ViamVjdCB0byBhbiBhZGRpdGlvbmFsIElQIHJpZ2h0cyBncmFudCBmb3VuZCBhdCBodHRwOi8vcG9seW1lci5naXRodWIuaW8vUEFURU5UUy50eHQKKi8KKGZ1bmN0aW9uKCl7LyoKCiBDb3B5cmlnaHQgKGMpIDIwMTYgVGhlIFBvbHltZXIgUHJvamVjdCBBdXRob3JzLiBBbGwgcmlnaHRzIHJlc2VydmVkLgogVGhpcyBjb2RlIG1heSBvbmx5IGJlIHVzZWQgdW5kZXIgdGhlIEJTRCBzdHlsZSBsaWNlbnNlIGZvdW5kIGF0IGh0dHA6Ly9wb2x5bWVyLmdpdGh1Yi5pby9MSUNFTlNFLnR4dAogVGhlIGNvbXBsZXRlIHNldCBvZiBhdXRob3JzIG1heSBiZSBmb3VuZCBhdCBodHRwOi8vcG9seW1lci5naXRodWIuaW8vQVVUSE9SUy50eHQKIFRoZSBjb21wbGV0ZSBzZXQgb2YgY29udHJpYnV0b3JzIG1heSBiZSBmb3VuZCBhdCBodHRwOi8vcG9seW1lci5naXRodWIuaW8vQ09OVFJJQlVUT1JTLnR4dAogQ29kZSBkaXN0cmlidXRlZCBieSBHb29nbGUgYXMgcGFydCBvZiB0aGUgcG9seW1lciBwcm9qZWN0IGlzIGFsc28KIHN1YmplY3QgdG8gYW4gYWRkaXRpb25hbCBJUCByaWdodHMgZ3JhbnQgZm91bmQgYXQgaHR0cDovL3BvbHltZXIuZ2l0aHViLmlvL1BBVEVOVFMudHh0CiovCid1c2Ugc3RyaWN0Jzt2YXIgbixwPSJ1bmRlZmluZWQiIT10eXBlb2Ygd2luZG93JiZ3aW5kb3c9PT10aGlzP3RoaXM6InVuZGVmaW5lZCIhPXR5cGVvZiBnbG9iYWwmJm51bGwhPWdsb2JhbD9nbG9iYWw6dGhpcyxhYT0iZnVuY3Rpb24iPT10eXBlb2YgT2JqZWN0LmRlZmluZVByb3BlcnRpZXM/T2JqZWN0LmRlZmluZVByb3BlcnR5OmZ1bmN0aW9uKGEsYixjKXthIT1BcnJheS5wcm90b3R5cGUmJmEhPU9iamVjdC5wcm90b3R5cGUmJihhW2JdPWMudmFsdWUpfTtmdW5jdGlvbiBiYSgpe2JhPWZ1bmN0aW9uKCl7fTtwLlN5bWJvbHx8KHAuU3ltYm9sPWNhKX12YXIgY2E9ZnVuY3Rpb24oKXt2YXIgYT0wO3JldHVybiBmdW5jdGlvbihiKXtyZXR1cm4ianNjb21wX3N5bWJvbF8iKyhifHwiIikrYSsrfX0oKTsKZnVuY3Rpb24gZGEoKXtiYSgpO3ZhciBhPXAuU3ltYm9sLml0ZXJhdG9yO2F8fChhPXAuU3ltYm9sLml0ZXJhdG9yPXAuU3ltYm9sKCJpdGVyYXRvciIpKTsiZnVuY3Rpb24iIT10eXBlb2YgQXJyYXkucHJvdG90eXBlW2FdJiZhYShBcnJheS5wcm90b3R5cGUsYSx7Y29uZmlndXJhYmxlOiEwLHdyaXRhYmxlOiEwLHZhbHVlOmZ1bmN0aW9uKCl7cmV0dXJuIGVhKHRoaXMpfX0pO2RhPWZ1bmN0aW9uKCl7fX1mdW5jdGlvbiBlYShhKXt2YXIgYj0wO3JldHVybiBmYShmdW5jdGlvbigpe3JldHVybiBiPGEubGVuZ3RoP3tkb25lOiExLHZhbHVlOmFbYisrXX06e2RvbmU6ITB9fSl9ZnVuY3Rpb24gZmEoYSl7ZGEoKTthPXtuZXh0OmF9O2FbcC5TeW1ib2wuaXRlcmF0b3JdPWZ1bmN0aW9uKCl7cmV0dXJuIHRoaXN9O3JldHVybiBhfWZ1bmN0aW9uIGlhKGEpe2RhKCk7dmFyIGI9YVtTeW1ib2wuaXRlcmF0b3JdO3JldHVybiBiP2IuY2FsbChhKTplYShhKX0KZnVuY3Rpb24gamEoYSl7Zm9yKHZhciBiLGM9W107IShiPWEubmV4dCgpKS5kb25lOyljLnB1c2goYi52YWx1ZSk7cmV0dXJuIGN9CihmdW5jdGlvbigpe2lmKCFmdW5jdGlvbigpe3ZhciBhPWRvY3VtZW50LmNyZWF0ZUV2ZW50KCJFdmVudCIpO2EuaW5pdEV2ZW50KCJmb28iLCEwLCEwKTthLnByZXZlbnREZWZhdWx0KCk7cmV0dXJuIGEuZGVmYXVsdFByZXZlbnRlZH0oKSl7dmFyIGE9RXZlbnQucHJvdG90eXBlLnByZXZlbnREZWZhdWx0O0V2ZW50LnByb3RvdHlwZS5wcmV2ZW50RGVmYXVsdD1mdW5jdGlvbigpe3RoaXMuY2FuY2VsYWJsZSYmKGEuY2FsbCh0aGlzKSxPYmplY3QuZGVmaW5lUHJvcGVydHkodGhpcywiZGVmYXVsdFByZXZlbnRlZCIse2dldDpmdW5jdGlvbigpe3JldHVybiEwfSxjb25maWd1cmFibGU6ITB9KSl9fXZhciBiPS9UcmlkZW50Ly50ZXN0KG5hdmlnYXRvci51c2VyQWdlbnQpO2lmKCF3aW5kb3cuQ3VzdG9tRXZlbnR8fGImJiJmdW5jdGlvbiIhPT10eXBlb2Ygd2luZG93LkN1c3RvbUV2ZW50KXdpbmRvdy5DdXN0b21FdmVudD1mdW5jdGlvbihhLGIpe2I9Ynx8e307dmFyIGM9ZG9jdW1lbnQuY3JlYXRlRXZlbnQoIkN1c3RvbUV2ZW50Iik7CmMuaW5pdEN1c3RvbUV2ZW50KGEsISFiLmJ1YmJsZXMsISFiLmNhbmNlbGFibGUsYi5kZXRhaWwpO3JldHVybiBjfSx3aW5kb3cuQ3VzdG9tRXZlbnQucHJvdG90eXBlPXdpbmRvdy5FdmVudC5wcm90b3R5cGU7aWYoIXdpbmRvdy5FdmVudHx8YiYmImZ1bmN0aW9uIiE9PXR5cGVvZiB3aW5kb3cuRXZlbnQpe3ZhciBjPXdpbmRvdy5FdmVudDt3aW5kb3cuRXZlbnQ9ZnVuY3Rpb24oYSxiKXtiPWJ8fHt9O3ZhciBjPWRvY3VtZW50LmNyZWF0ZUV2ZW50KCJFdmVudCIpO2MuaW5pdEV2ZW50KGEsISFiLmJ1YmJsZXMsISFiLmNhbmNlbGFibGUpO3JldHVybiBjfTtpZihjKWZvcih2YXIgZCBpbiBjKXdpbmRvdy5FdmVudFtkXT1jW2RdO3dpbmRvdy5FdmVudC5wcm90b3R5cGU9Yy5wcm90b3R5cGV9aWYoIXdpbmRvdy5Nb3VzZUV2ZW50fHxiJiYiZnVuY3Rpb24iIT09dHlwZW9mIHdpbmRvdy5Nb3VzZUV2ZW50KXtiPXdpbmRvdy5Nb3VzZUV2ZW50O3dpbmRvdy5Nb3VzZUV2ZW50PWZ1bmN0aW9uKGEsCmIpe2I9Ynx8e307dmFyIGM9ZG9jdW1lbnQuY3JlYXRlRXZlbnQoIk1vdXNlRXZlbnQiKTtjLmluaXRNb3VzZUV2ZW50KGEsISFiLmJ1YmJsZXMsISFiLmNhbmNlbGFibGUsYi52aWV3fHx3aW5kb3csYi5kZXRhaWwsYi5zY3JlZW5YLGIuc2NyZWVuWSxiLmNsaWVudFgsYi5jbGllbnRZLGIuY3RybEtleSxiLmFsdEtleSxiLnNoaWZ0S2V5LGIubWV0YUtleSxiLmJ1dHRvbixiLnJlbGF0ZWRUYXJnZXQpO3JldHVybiBjfTtpZihiKWZvcihkIGluIGIpd2luZG93Lk1vdXNlRXZlbnRbZF09YltkXTt3aW5kb3cuTW91c2VFdmVudC5wcm90b3R5cGU9Yi5wcm90b3R5cGV9QXJyYXkuZnJvbXx8KEFycmF5LmZyb209ZnVuY3Rpb24oYSl7cmV0dXJuW10uc2xpY2UuY2FsbChhKX0pO09iamVjdC5hc3NpZ258fChPYmplY3QuYXNzaWduPWZ1bmN0aW9uKGEsYil7Zm9yKHZhciBjPVtdLnNsaWNlLmNhbGwoYXJndW1lbnRzLDEpLGQ9MCxlO2Q8Yy5sZW5ndGg7ZCsrKWlmKGU9Y1tkXSlmb3IodmFyIGY9CmEsbT1lLHE9T2JqZWN0LmdldE93blByb3BlcnR5TmFtZXMobSkseD0wO3g8cS5sZW5ndGg7eCsrKWU9cVt4XSxmW2VdPW1bZV07cmV0dXJuIGF9KX0pKHdpbmRvdy5XZWJDb21wb25lbnRzKTsoZnVuY3Rpb24oKXtmdW5jdGlvbiBhKCl7fWZ1bmN0aW9uIGIoYSxiKXtpZighYS5jaGlsZE5vZGVzLmxlbmd0aClyZXR1cm5bXTtzd2l0Y2goYS5ub2RlVHlwZSl7Y2FzZSBOb2RlLkRPQ1VNRU5UX05PREU6cmV0dXJuIHVhLmNhbGwoYSxiKTtjYXNlIE5vZGUuRE9DVU1FTlRfRlJBR01FTlRfTk9ERTpyZXR1cm4gbGIuY2FsbChhLGIpO2RlZmF1bHQ6cmV0dXJuIFUuY2FsbChhLGIpfX12YXIgYz0idW5kZWZpbmVkIj09PXR5cGVvZiBIVE1MVGVtcGxhdGVFbGVtZW50LGQ9IShkb2N1bWVudC5jcmVhdGVEb2N1bWVudEZyYWdtZW50KCkuY2xvbmVOb2RlKClpbnN0YW5jZW9mIERvY3VtZW50RnJhZ21lbnQpLGU9ITE7L1RyaWRlbnQvLnRlc3QobmF2aWdhdG9yLnVzZXJBZ2VudCkmJmZ1bmN0aW9uKCl7ZnVuY3Rpb24gYShhLGIpe2lmKGEgaW5zdGFuY2VvZiBEb2N1bWVudEZyYWdtZW50KWZvcih2YXIgZDtkPWEuZmlyc3RDaGlsZDspYy5jYWxsKHRoaXMsZCxiKTtlbHNlIGMuY2FsbCh0aGlzLAphLGIpO3JldHVybiBhfWU9ITA7dmFyIGI9Tm9kZS5wcm90b3R5cGUuY2xvbmVOb2RlO05vZGUucHJvdG90eXBlLmNsb25lTm9kZT1mdW5jdGlvbihhKXthPWIuY2FsbCh0aGlzLGEpO3RoaXMgaW5zdGFuY2VvZiBEb2N1bWVudEZyYWdtZW50JiYoYS5fX3Byb3RvX189RG9jdW1lbnRGcmFnbWVudC5wcm90b3R5cGUpO3JldHVybiBhfTtEb2N1bWVudEZyYWdtZW50LnByb3RvdHlwZS5xdWVyeVNlbGVjdG9yQWxsPUhUTUxFbGVtZW50LnByb3RvdHlwZS5xdWVyeVNlbGVjdG9yQWxsO0RvY3VtZW50RnJhZ21lbnQucHJvdG90eXBlLnF1ZXJ5U2VsZWN0b3I9SFRNTEVsZW1lbnQucHJvdG90eXBlLnF1ZXJ5U2VsZWN0b3I7T2JqZWN0LmRlZmluZVByb3BlcnRpZXMoRG9jdW1lbnRGcmFnbWVudC5wcm90b3R5cGUse25vZGVUeXBlOntnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gTm9kZS5ET0NVTUVOVF9GUkFHTUVOVF9OT0RFfSxjb25maWd1cmFibGU6ITB9LGxvY2FsTmFtZTp7Z2V0OmZ1bmN0aW9uKCl7fSwKY29uZmlndXJhYmxlOiEwfSxub2RlTmFtZTp7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIiNkb2N1bWVudC1mcmFnbWVudCJ9LGNvbmZpZ3VyYWJsZTohMH19KTt2YXIgYz1Ob2RlLnByb3RvdHlwZS5pbnNlcnRCZWZvcmU7Tm9kZS5wcm90b3R5cGUuaW5zZXJ0QmVmb3JlPWE7dmFyIGQ9Tm9kZS5wcm90b3R5cGUuYXBwZW5kQ2hpbGQ7Tm9kZS5wcm90b3R5cGUuYXBwZW5kQ2hpbGQ9ZnVuY3Rpb24oYil7YiBpbnN0YW5jZW9mIERvY3VtZW50RnJhZ21lbnQ/YS5jYWxsKHRoaXMsYixudWxsKTpkLmNhbGwodGhpcyxiKTtyZXR1cm4gYn07dmFyIGY9Tm9kZS5wcm90b3R5cGUucmVtb3ZlQ2hpbGQsZz1Ob2RlLnByb3RvdHlwZS5yZXBsYWNlQ2hpbGQ7Tm9kZS5wcm90b3R5cGUucmVwbGFjZUNoaWxkPWZ1bmN0aW9uKGIsYyl7YiBpbnN0YW5jZW9mIERvY3VtZW50RnJhZ21lbnQ/KGEuY2FsbCh0aGlzLGIsYyksZi5jYWxsKHRoaXMsYykpOmcuY2FsbCh0aGlzLGIsYyk7cmV0dXJuIGN9O0RvY3VtZW50LnByb3RvdHlwZS5jcmVhdGVEb2N1bWVudEZyYWdtZW50PQpmdW5jdGlvbigpe3ZhciBhPXRoaXMuY3JlYXRlRWxlbWVudCgiZGYiKTthLl9fcHJvdG9fXz1Eb2N1bWVudEZyYWdtZW50LnByb3RvdHlwZTtyZXR1cm4gYX07dmFyIGg9RG9jdW1lbnQucHJvdG90eXBlLmltcG9ydE5vZGU7RG9jdW1lbnQucHJvdG90eXBlLmltcG9ydE5vZGU9ZnVuY3Rpb24oYSxiKXtiPWguY2FsbCh0aGlzLGEsYnx8ITEpO2EgaW5zdGFuY2VvZiBEb2N1bWVudEZyYWdtZW50JiYoYi5fX3Byb3RvX189RG9jdW1lbnRGcmFnbWVudC5wcm90b3R5cGUpO3JldHVybiBifX0oKTt2YXIgZj1Ob2RlLnByb3RvdHlwZS5jbG9uZU5vZGUsZz1Eb2N1bWVudC5wcm90b3R5cGUuY3JlYXRlRWxlbWVudCxoPURvY3VtZW50LnByb3RvdHlwZS5pbXBvcnROb2RlLGs9Tm9kZS5wcm90b3R5cGUucmVtb3ZlQ2hpbGQsbD1Ob2RlLnByb3RvdHlwZS5hcHBlbmRDaGlsZCxtPU5vZGUucHJvdG90eXBlLnJlcGxhY2VDaGlsZCxxPURPTVBhcnNlci5wcm90b3R5cGUucGFyc2VGcm9tU3RyaW5nLAp4PU9iamVjdC5nZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3Iod2luZG93LkhUTUxFbGVtZW50LnByb3RvdHlwZSwiaW5uZXJIVE1MIil8fHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5pbm5lckhUTUx9LHNldDpmdW5jdGlvbihhKXt0aGlzLmlubmVySFRNTD1hfX0sTT1PYmplY3QuZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yKHdpbmRvdy5Ob2RlLnByb3RvdHlwZSwiY2hpbGROb2RlcyIpfHx7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuY2hpbGROb2Rlc319LFU9RWxlbWVudC5wcm90b3R5cGUucXVlcnlTZWxlY3RvckFsbCx1YT1Eb2N1bWVudC5wcm90b3R5cGUucXVlcnlTZWxlY3RvckFsbCxsYj1Eb2N1bWVudEZyYWdtZW50LnByb3RvdHlwZS5xdWVyeVNlbGVjdG9yQWxsLG1iPWZ1bmN0aW9uKCl7aWYoIWMpe3ZhciBhPWRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoInRlbXBsYXRlIiksYj1kb2N1bWVudC5jcmVhdGVFbGVtZW50KCJ0ZW1wbGF0ZSIpO2IuY29udGVudC5hcHBlbmRDaGlsZChkb2N1bWVudC5jcmVhdGVFbGVtZW50KCJkaXYiKSk7CmEuY29udGVudC5hcHBlbmRDaGlsZChiKTthPWEuY2xvbmVOb2RlKCEwKTtyZXR1cm4gMD09PWEuY29udGVudC5jaGlsZE5vZGVzLmxlbmd0aHx8MD09PWEuY29udGVudC5maXJzdENoaWxkLmNvbnRlbnQuY2hpbGROb2Rlcy5sZW5ndGh8fGR9fSgpO2lmKGMpe3ZhciBTPWRvY3VtZW50LmltcGxlbWVudGF0aW9uLmNyZWF0ZUhUTUxEb2N1bWVudCgidGVtcGxhdGUiKSxDPSEwLFY9ZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgic3R5bGUiKTtWLnRleHRDb250ZW50PSJ0ZW1wbGF0ZXtkaXNwbGF5Om5vbmU7fSI7dmFyIGhhPWRvY3VtZW50LmhlYWQ7aGEuaW5zZXJ0QmVmb3JlKFYsaGEuZmlyc3RFbGVtZW50Q2hpbGQpO2EucHJvdG90eXBlPU9iamVjdC5jcmVhdGUoSFRNTEVsZW1lbnQucHJvdG90eXBlKTt2YXIgdmE9IWRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoImRpdiIpLmhhc093blByb3BlcnR5KCJpbm5lckhUTUwiKTthLkc9ZnVuY3Rpb24oYil7aWYoIWIuY29udGVudCYmYi5uYW1lc3BhY2VVUkk9PT0KZG9jdW1lbnQuZG9jdW1lbnRFbGVtZW50Lm5hbWVzcGFjZVVSSSl7Yi5jb250ZW50PVMuY3JlYXRlRG9jdW1lbnRGcmFnbWVudCgpO2Zvcih2YXIgYztjPWIuZmlyc3RDaGlsZDspbC5jYWxsKGIuY29udGVudCxjKTtpZih2YSliLl9fcHJvdG9fXz1hLnByb3RvdHlwZTtlbHNlIGlmKGIuY2xvbmVOb2RlPWZ1bmN0aW9uKGIpe3JldHVybiBhLmEodGhpcyxiKX0sQyl0cnl7UChiKSxXKGIpfWNhdGNoKFRnKXtDPSExfWEuQyhiLmNvbnRlbnQpfX07dmFyIFg9e29wdGlvbjpbInNlbGVjdCJdLHRoZWFkOlsidGFibGUiXSxjb2w6WyJjb2xncm91cCIsInRhYmxlIl0sdHI6WyJ0Ym9keSIsInRhYmxlIl0sdGg6WyJ0ciIsInRib2R5IiwidGFibGUiXSx0ZDpbInRyIiwidGJvZHkiLCJ0YWJsZSJdfSxQPWZ1bmN0aW9uKGIpe09iamVjdC5kZWZpbmVQcm9wZXJ0eShiLCJpbm5lckhUTUwiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gbmIodGhpcyl9LHNldDpmdW5jdGlvbihiKXt2YXIgYz1YWygvPChbYS16XVteL1wwPlx4MjBcdFxyXG5cZl0rKS9pLmV4ZWMoYil8fApbIiIsIiJdKVsxXS50b0xvd2VyQ2FzZSgpXTtpZihjKWZvcih2YXIgZD0wO2Q8Yy5sZW5ndGg7ZCsrKWI9IjwiK2NbZF0rIj4iK2IrIjwvIitjW2RdKyI+IjtTLmJvZHkuaW5uZXJIVE1MPWI7Zm9yKGEuQyhTKTt0aGlzLmNvbnRlbnQuZmlyc3RDaGlsZDspay5jYWxsKHRoaXMuY29udGVudCx0aGlzLmNvbnRlbnQuZmlyc3RDaGlsZCk7Yj1TLmJvZHk7aWYoYylmb3IoZD0wO2Q8Yy5sZW5ndGg7ZCsrKWI9Yi5sYXN0Q2hpbGQ7Zm9yKDtiLmZpcnN0Q2hpbGQ7KWwuY2FsbCh0aGlzLmNvbnRlbnQsYi5maXJzdENoaWxkKX0sY29uZmlndXJhYmxlOiEwfSl9LFc9ZnVuY3Rpb24oYSl7T2JqZWN0LmRlZmluZVByb3BlcnR5KGEsIm91dGVySFRNTCIse2dldDpmdW5jdGlvbigpe3JldHVybiI8dGVtcGxhdGU+Iit0aGlzLmlubmVySFRNTCsiPC90ZW1wbGF0ZT4ifSxzZXQ6ZnVuY3Rpb24oYSl7aWYodGhpcy5wYXJlbnROb2RlKXtTLmJvZHkuaW5uZXJIVE1MPWE7Zm9yKGE9dGhpcy5vd25lckRvY3VtZW50LmNyZWF0ZURvY3VtZW50RnJhZ21lbnQoKTtTLmJvZHkuZmlyc3RDaGlsZDspbC5jYWxsKGEsClMuYm9keS5maXJzdENoaWxkKTttLmNhbGwodGhpcy5wYXJlbnROb2RlLGEsdGhpcyl9ZWxzZSB0aHJvdyBFcnJvcigiRmFpbGVkIHRvIHNldCB0aGUgJ291dGVySFRNTCcgcHJvcGVydHkgb24gJ0VsZW1lbnQnOiBUaGlzIGVsZW1lbnQgaGFzIG5vIHBhcmVudCBub2RlLiIpO30sY29uZmlndXJhYmxlOiEwfSl9O1AoYS5wcm90b3R5cGUpO1coYS5wcm90b3R5cGUpO2EuQz1mdW5jdGlvbihjKXtjPWIoYywidGVtcGxhdGUiKTtmb3IodmFyIGQ9MCxlPWMubGVuZ3RoLGY7ZDxlJiYoZj1jW2RdKTtkKyspYS5HKGYpfTtkb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCJET01Db250ZW50TG9hZGVkIixmdW5jdGlvbigpe2EuQyhkb2N1bWVudCl9KTtEb2N1bWVudC5wcm90b3R5cGUuY3JlYXRlRWxlbWVudD1mdW5jdGlvbigpe3ZhciBiPWcuYXBwbHkodGhpcyxhcmd1bWVudHMpOyJ0ZW1wbGF0ZSI9PT1iLmxvY2FsTmFtZSYmYS5HKGIpO3JldHVybiBifTtET01QYXJzZXIucHJvdG90eXBlLnBhcnNlRnJvbVN0cmluZz0KZnVuY3Rpb24oKXt2YXIgYj1xLmFwcGx5KHRoaXMsYXJndW1lbnRzKTthLkMoYik7cmV0dXJuIGJ9O09iamVjdC5kZWZpbmVQcm9wZXJ0eShIVE1MRWxlbWVudC5wcm90b3R5cGUsImlubmVySFRNTCIse2dldDpmdW5jdGlvbigpe3JldHVybiBuYih0aGlzKX0sc2V0OmZ1bmN0aW9uKGIpe3guc2V0LmNhbGwodGhpcyxiKTthLkModGhpcyl9LGNvbmZpZ3VyYWJsZTohMCxlbnVtZXJhYmxlOiEwfSk7dmFyIFZlPS9bJlx1MDBBMCJdL2cseWM9L1smXHUwMEEwPD5dL2csemM9ZnVuY3Rpb24oYSl7c3dpdGNoKGEpe2Nhc2UgIiYiOnJldHVybiImYW1wOyI7Y2FzZSAiPCI6cmV0dXJuIiZsdDsiO2Nhc2UgIj4iOnJldHVybiImZ3Q7IjtjYXNlICciJzpyZXR1cm4iJnF1b3Q7IjtjYXNlICJcdTAwYTAiOnJldHVybiImbmJzcDsifX07Vj1mdW5jdGlvbihhKXtmb3IodmFyIGI9e30sYz0wO2M8YS5sZW5ndGg7YysrKWJbYVtjXV09ITA7cmV0dXJuIGJ9O3ZhciBXZT1WKCJhcmVhIGJhc2UgYnIgY29sIGNvbW1hbmQgZW1iZWQgaHIgaW1nIGlucHV0IGtleWdlbiBsaW5rIG1ldGEgcGFyYW0gc291cmNlIHRyYWNrIHdiciIuc3BsaXQoIiAiKSksClhlPVYoInN0eWxlIHNjcmlwdCB4bXAgaWZyYW1lIG5vZW1iZWQgbm9mcmFtZXMgcGxhaW50ZXh0IG5vc2NyaXB0Ii5zcGxpdCgiICIpKSxuYj1mdW5jdGlvbihhLGIpeyJ0ZW1wbGF0ZSI9PT1hLmxvY2FsTmFtZSYmKGE9YS5jb250ZW50KTtmb3IodmFyIGM9IiIsZD1iP2IoYSk6TS5nZXQuY2FsbChhKSxlPTAsZj1kLmxlbmd0aCxnO2U8ZiYmKGc9ZFtlXSk7ZSsrKXthOnt2YXIgaD1nO3ZhciBrPWE7dmFyIGw9Yjtzd2l0Y2goaC5ub2RlVHlwZSl7Y2FzZSBOb2RlLkVMRU1FTlRfTk9ERTpmb3IodmFyIFA9aC5sb2NhbE5hbWUsbT0iPCIrUCxXPWguYXR0cmlidXRlcyxxPTA7az1XW3FdO3ErKyltKz0iICIray5uYW1lKyc9Iicray52YWx1ZS5yZXBsYWNlKFZlLHpjKSsnIic7bSs9Ij4iO2g9V2VbUF0/bTptK25iKGgsbCkrIjwvIitQKyI+IjticmVhayBhO2Nhc2UgTm9kZS5URVhUX05PREU6aD1oLmRhdGE7aD1rJiZYZVtrLmxvY2FsTmFtZV0/aDpoLnJlcGxhY2UoeWMsemMpO2JyZWFrIGE7CmNhc2UgTm9kZS5DT01NRU5UX05PREU6aD0iXHgzYyEtLSIraC5kYXRhKyItLVx4M2UiO2JyZWFrIGE7ZGVmYXVsdDp0aHJvdyB3aW5kb3cuY29uc29sZS5lcnJvcihoKSxFcnJvcigibm90IGltcGxlbWVudGVkIik7fX1jKz1ofXJldHVybiBjfX1pZihjfHxtYil7YS5hPWZ1bmN0aW9uKGEsYil7dmFyIGM9Zi5jYWxsKGEsITEpO3RoaXMuRyYmdGhpcy5HKGMpO2ImJihsLmNhbGwoYy5jb250ZW50LGYuY2FsbChhLmNvbnRlbnQsITApKSxvYihjLmNvbnRlbnQsYS5jb250ZW50KSk7cmV0dXJuIGN9O3ZhciBvYj1mdW5jdGlvbihjLGQpe2lmKGQucXVlcnlTZWxlY3RvckFsbCYmKGQ9YihkLCJ0ZW1wbGF0ZSIpLDAhPT1kLmxlbmd0aCkpe2M9YihjLCJ0ZW1wbGF0ZSIpO2Zvcih2YXIgZT0wLGY9Yy5sZW5ndGgsZyxoO2U8ZjtlKyspaD1kW2VdLGc9Y1tlXSxhJiZhLkcmJmEuRyhoKSxtLmNhbGwoZy5wYXJlbnROb2RlLFllLmNhbGwoaCwhMCksZyl9fSxZZT1Ob2RlLnByb3RvdHlwZS5jbG9uZU5vZGU9CmZ1bmN0aW9uKGIpe2lmKCFlJiZkJiZ0aGlzIGluc3RhbmNlb2YgRG9jdW1lbnRGcmFnbWVudClpZihiKXZhciBjPVplLmNhbGwodGhpcy5vd25lckRvY3VtZW50LHRoaXMsITApO2Vsc2UgcmV0dXJuIHRoaXMub3duZXJEb2N1bWVudC5jcmVhdGVEb2N1bWVudEZyYWdtZW50KCk7ZWxzZSB0aGlzLm5vZGVUeXBlPT09Tm9kZS5FTEVNRU5UX05PREUmJiJ0ZW1wbGF0ZSI9PT10aGlzLmxvY2FsTmFtZSYmdGhpcy5uYW1lc3BhY2VVUkk9PWRvY3VtZW50LmRvY3VtZW50RWxlbWVudC5uYW1lc3BhY2VVUkk/Yz1hLmEodGhpcyxiKTpjPWYuY2FsbCh0aGlzLGIpO2ImJm9iKGMsdGhpcyk7cmV0dXJuIGN9LFplPURvY3VtZW50LnByb3RvdHlwZS5pbXBvcnROb2RlPWZ1bmN0aW9uKGMsZCl7ZD1kfHwhMTtpZigidGVtcGxhdGUiPT09Yy5sb2NhbE5hbWUpcmV0dXJuIGEuYShjLGQpO3ZhciBlPWguY2FsbCh0aGlzLGMsZCk7aWYoZCl7b2IoZSxjKTtjPWIoZSwnc2NyaXB0Om5vdChbdHlwZV0pLHNjcmlwdFt0eXBlPSJhcHBsaWNhdGlvbi9qYXZhc2NyaXB0Il0sc2NyaXB0W3R5cGU9InRleHQvamF2YXNjcmlwdCJdJyk7CmZvcih2YXIgZixrPTA7azxjLmxlbmd0aDtrKyspe2Y9Y1trXTtkPWcuY2FsbChkb2N1bWVudCwic2NyaXB0Iik7ZC50ZXh0Q29udGVudD1mLnRleHRDb250ZW50O2Zvcih2YXIgbD1mLmF0dHJpYnV0ZXMsUD0wLFc7UDxsLmxlbmd0aDtQKyspVz1sW1BdLGQuc2V0QXR0cmlidXRlKFcubmFtZSxXLnZhbHVlKTttLmNhbGwoZi5wYXJlbnROb2RlLGQsZil9fXJldHVybiBlfX1jJiYod2luZG93LkhUTUxUZW1wbGF0ZUVsZW1lbnQ9YSl9KSgpO3ZhciBrYT1zZXRUaW1lb3V0O2Z1bmN0aW9uIGxhKCl7fWZ1bmN0aW9uIG1hKGEsYil7cmV0dXJuIGZ1bmN0aW9uKCl7YS5hcHBseShiLGFyZ3VtZW50cyl9fWZ1bmN0aW9uIHIoYSl7aWYoISh0aGlzIGluc3RhbmNlb2YgcikpdGhyb3cgbmV3IFR5cGVFcnJvcigiUHJvbWlzZXMgbXVzdCBiZSBjb25zdHJ1Y3RlZCB2aWEgbmV3Iik7aWYoImZ1bmN0aW9uIiE9PXR5cGVvZiBhKXRocm93IG5ldyBUeXBlRXJyb3IoIm5vdCBhIGZ1bmN0aW9uIik7dGhpcy51PTA7dGhpcy5tYT0hMTt0aGlzLmg9dm9pZCAwO3RoaXMuST1bXTtuYShhLHRoaXMpfQpmdW5jdGlvbiBvYShhLGIpe2Zvcig7Mz09PWEudTspYT1hLmg7MD09PWEudT9hLkkucHVzaChiKTooYS5tYT0hMCxwYShmdW5jdGlvbigpe3ZhciBjPTE9PT1hLnU/Yi5OYTpiLk9hO2lmKG51bGw9PT1jKSgxPT09YS51P3FhOnJhKShiLmdhLGEuaCk7ZWxzZXt0cnl7dmFyIGQ9YyhhLmgpfWNhdGNoKGUpe3JhKGIuZ2EsZSk7cmV0dXJufXFhKGIuZ2EsZCl9fSkpfWZ1bmN0aW9uIHFhKGEsYil7dHJ5e2lmKGI9PT1hKXRocm93IG5ldyBUeXBlRXJyb3IoIkEgcHJvbWlzZSBjYW5ub3QgYmUgcmVzb2x2ZWQgd2l0aCBpdHNlbGYuIik7aWYoYiYmKCJvYmplY3QiPT09dHlwZW9mIGJ8fCJmdW5jdGlvbiI9PT10eXBlb2YgYikpe3ZhciBjPWIudGhlbjtpZihiIGluc3RhbmNlb2Ygcil7YS51PTM7YS5oPWI7c2EoYSk7cmV0dXJufWlmKCJmdW5jdGlvbiI9PT10eXBlb2YgYyl7bmEobWEoYyxiKSxhKTtyZXR1cm59fWEudT0xO2EuaD1iO3NhKGEpfWNhdGNoKGQpe3JhKGEsZCl9fQpmdW5jdGlvbiByYShhLGIpe2EudT0yO2EuaD1iO3NhKGEpfWZ1bmN0aW9uIHNhKGEpezI9PT1hLnUmJjA9PT1hLkkubGVuZ3RoJiZwYShmdW5jdGlvbigpe2EubWF8fCJ1bmRlZmluZWQiIT09dHlwZW9mIGNvbnNvbGUmJmNvbnNvbGUmJmNvbnNvbGUud2FybigiUG9zc2libGUgVW5oYW5kbGVkIFByb21pc2UgUmVqZWN0aW9uOiIsYS5oKX0pO2Zvcih2YXIgYj0wLGM9YS5JLmxlbmd0aDtiPGM7YisrKW9hKGEsYS5JW2JdKTthLkk9bnVsbH1mdW5jdGlvbiB0YShhLGIsYyl7dGhpcy5OYT0iZnVuY3Rpb24iPT09dHlwZW9mIGE/YTpudWxsO3RoaXMuT2E9ImZ1bmN0aW9uIj09PXR5cGVvZiBiP2I6bnVsbDt0aGlzLmdhPWN9ZnVuY3Rpb24gbmEoYSxiKXt2YXIgYz0hMTt0cnl7YShmdW5jdGlvbihhKXtjfHwoYz0hMCxxYShiLGEpKX0sZnVuY3Rpb24oYSl7Y3x8KGM9ITAscmEoYixhKSl9KX1jYXRjaChkKXtjfHwoYz0hMCxyYShiLGQpKX19CnIucHJvdG90eXBlWyJjYXRjaCJdPWZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLnRoZW4obnVsbCxhKX07ci5wcm90b3R5cGUudGhlbj1mdW5jdGlvbihhLGIpe3ZhciBjPW5ldyB0aGlzLmNvbnN0cnVjdG9yKGxhKTtvYSh0aGlzLG5ldyB0YShhLGIsYykpO3JldHVybiBjfTtyLnByb3RvdHlwZVsiZmluYWxseSJdPWZ1bmN0aW9uKGEpe3ZhciBiPXRoaXMuY29uc3RydWN0b3I7cmV0dXJuIHRoaXMudGhlbihmdW5jdGlvbihjKXtyZXR1cm4gYi5yZXNvbHZlKGEoKSkudGhlbihmdW5jdGlvbigpe3JldHVybiBjfSl9LGZ1bmN0aW9uKGMpe3JldHVybiBiLnJlc29sdmUoYSgpKS50aGVuKGZ1bmN0aW9uKCl7cmV0dXJuIGIucmVqZWN0KGMpfSl9KX07CmZ1bmN0aW9uIHdhKGEpe3JldHVybiBuZXcgcihmdW5jdGlvbihiLGMpe2Z1bmN0aW9uIGQoYSxnKXt0cnl7aWYoZyYmKCJvYmplY3QiPT09dHlwZW9mIGd8fCJmdW5jdGlvbiI9PT10eXBlb2YgZykpe3ZhciBoPWcudGhlbjtpZigiZnVuY3Rpb24iPT09dHlwZW9mIGgpe2guY2FsbChnLGZ1bmN0aW9uKGIpe2QoYSxiKX0sYyk7cmV0dXJufX1lW2FdPWc7MD09PS0tZiYmYihlKX1jYXRjaChtKXtjKG0pfX1pZighYXx8InVuZGVmaW5lZCI9PT10eXBlb2YgYS5sZW5ndGgpdGhyb3cgbmV3IFR5cGVFcnJvcigiUHJvbWlzZS5hbGwgYWNjZXB0cyBhbiBhcnJheSIpO3ZhciBlPUFycmF5LnByb3RvdHlwZS5zbGljZS5jYWxsKGEpO2lmKDA9PT1lLmxlbmd0aClyZXR1cm4gYihbXSk7Zm9yKHZhciBmPWUubGVuZ3RoLGc9MDtnPGUubGVuZ3RoO2crKylkKGcsZVtnXSl9KX0KZnVuY3Rpb24geGEoYSl7cmV0dXJuIGEmJiJvYmplY3QiPT09dHlwZW9mIGEmJmEuY29uc3RydWN0b3I9PT1yP2E6bmV3IHIoZnVuY3Rpb24oYil7YihhKX0pfWZ1bmN0aW9uIHlhKGEpe3JldHVybiBuZXcgcihmdW5jdGlvbihiLGMpe2MoYSl9KX1mdW5jdGlvbiB6YShhKXtyZXR1cm4gbmV3IHIoZnVuY3Rpb24oYixjKXtmb3IodmFyIGQ9MCxlPWEubGVuZ3RoO2Q8ZTtkKyspYVtkXS50aGVuKGIsYyl9KX12YXIgcGE9ImZ1bmN0aW9uIj09PXR5cGVvZiBzZXRJbW1lZGlhdGUmJmZ1bmN0aW9uKGEpe3NldEltbWVkaWF0ZShhKX18fGZ1bmN0aW9uKGEpe2thKGEsMCl9Oy8qCgpDb3B5cmlnaHQgKGMpIDIwMTcgVGhlIFBvbHltZXIgUHJvamVjdCBBdXRob3JzLiBBbGwgcmlnaHRzIHJlc2VydmVkLgpUaGlzIGNvZGUgbWF5IG9ubHkgYmUgdXNlZCB1bmRlciB0aGUgQlNEIHN0eWxlIGxpY2Vuc2UgZm91bmQgYXQgaHR0cDovL3BvbHltZXIuZ2l0aHViLmlvL0xJQ0VOU0UudHh0ClRoZSBjb21wbGV0ZSBzZXQgb2YgYXV0aG9ycyBtYXkgYmUgZm91bmQgYXQgaHR0cDovL3BvbHltZXIuZ2l0aHViLmlvL0FVVEhPUlMudHh0ClRoZSBjb21wbGV0ZSBzZXQgb2YgY29udHJpYnV0b3JzIG1heSBiZSBmb3VuZCBhdCBodHRwOi8vcG9seW1lci5naXRodWIuaW8vQ09OVFJJQlVUT1JTLnR4dApDb2RlIGRpc3RyaWJ1dGVkIGJ5IEdvb2dsZSBhcyBwYXJ0IG9mIHRoZSBwb2x5bWVyIHByb2plY3QgaXMgYWxzbwpzdWJqZWN0IHRvIGFuIGFkZGl0aW9uYWwgSVAgcmlnaHRzIGdyYW50IGZvdW5kIGF0IGh0dHA6Ly9wb2x5bWVyLmdpdGh1Yi5pby9QQVRFTlRTLnR4dAoqLwppZighd2luZG93LlByb21pc2Upe3dpbmRvdy5Qcm9taXNlPXI7ci5wcm90b3R5cGUudGhlbj1yLnByb3RvdHlwZS50aGVuO3IuYWxsPXdhO3IucmFjZT16YTtyLnJlc29sdmU9eGE7ci5yZWplY3Q9eWE7dmFyIEFhPWRvY3VtZW50LmNyZWF0ZVRleHROb2RlKCIiKSxCYT1bXTsobmV3IE11dGF0aW9uT2JzZXJ2ZXIoZnVuY3Rpb24oKXtmb3IodmFyIGE9QmEubGVuZ3RoLGI9MDtiPGE7YisrKUJhW2JdKCk7QmEuc3BsaWNlKDAsYSl9KSkub2JzZXJ2ZShBYSx7Y2hhcmFjdGVyRGF0YTohMH0pO3BhPWZ1bmN0aW9uKGEpe0JhLnB1c2goYSk7QWEudGV4dENvbnRlbnQ9MDxBYS50ZXh0Q29udGVudC5sZW5ndGg/IiI6ImEifX07KGZ1bmN0aW9uKGEpe2Z1bmN0aW9uIGIoYSxiKXtpZigiZnVuY3Rpb24iPT09dHlwZW9mIHdpbmRvdy5DdXN0b21FdmVudClyZXR1cm4gbmV3IEN1c3RvbUV2ZW50KGEsYik7dmFyIGM9ZG9jdW1lbnQuY3JlYXRlRXZlbnQoIkN1c3RvbUV2ZW50Iik7Yy5pbml0Q3VzdG9tRXZlbnQoYSwhIWIuYnViYmxlcywhIWIuY2FuY2VsYWJsZSxiLmRldGFpbCk7cmV0dXJuIGN9ZnVuY3Rpb24gYyhhKXtpZihNKXJldHVybiBhLm93bmVyRG9jdW1lbnQhPT1kb2N1bWVudD9hLm93bmVyRG9jdW1lbnQ6bnVsbDt2YXIgYj1hLl9faW1wb3J0RG9jO2lmKCFiJiZhLnBhcmVudE5vZGUpe2I9YS5wYXJlbnROb2RlO2lmKCJmdW5jdGlvbiI9PT10eXBlb2YgYi5jbG9zZXN0KWI9Yi5jbG9zZXN0KCJsaW5rW3JlbD1pbXBvcnRdIik7ZWxzZSBmb3IoOyFoKGIpJiYoYj1iLnBhcmVudE5vZGUpOyk7YS5fX2ltcG9ydERvYz1ifXJldHVybiBifWZ1bmN0aW9uIGQoYSl7dmFyIGI9bShkb2N1bWVudCwibGlua1tyZWw9aW1wb3J0XTpub3QoW2ltcG9ydC1kZXBlbmRlbmN5XSkiKSwKYz1iLmxlbmd0aDtjP3EoYixmdW5jdGlvbihiKXtyZXR1cm4gZyhiLGZ1bmN0aW9uKCl7MD09PS0tYyYmYSgpfSl9KTphKCl9ZnVuY3Rpb24gZShhKXtmdW5jdGlvbiBiKCl7ImxvYWRpbmciIT09ZG9jdW1lbnQucmVhZHlTdGF0ZSYmZG9jdW1lbnQuYm9keSYmKGRvY3VtZW50LnJlbW92ZUV2ZW50TGlzdGVuZXIoInJlYWR5c3RhdGVjaGFuZ2UiLGIpLGEoKSl9ZG9jdW1lbnQuYWRkRXZlbnRMaXN0ZW5lcigicmVhZHlzdGF0ZWNoYW5nZSIsYik7YigpfWZ1bmN0aW9uIGYoYSl7ZShmdW5jdGlvbigpe3JldHVybiBkKGZ1bmN0aW9uKCl7cmV0dXJuIGEmJmEoKX0pfSl9ZnVuY3Rpb24gZyhhLGIpe2lmKGEuX19sb2FkZWQpYiYmYigpO2Vsc2UgaWYoInNjcmlwdCI9PT1hLmxvY2FsTmFtZSYmIWEuc3JjfHwic3R5bGUiPT09YS5sb2NhbE5hbWUmJiFhLmZpcnN0Q2hpbGQpYS5fX2xvYWRlZD0hMCxiJiZiKCk7ZWxzZXt2YXIgYz1mdW5jdGlvbihkKXthLnJlbW92ZUV2ZW50TGlzdGVuZXIoZC50eXBlLApjKTthLl9fbG9hZGVkPSEwO2ImJmIoKX07YS5hZGRFdmVudExpc3RlbmVyKCJsb2FkIixjKTtoYSYmInN0eWxlIj09PWEubG9jYWxOYW1lfHxhLmFkZEV2ZW50TGlzdGVuZXIoImVycm9yIixjKX19ZnVuY3Rpb24gaChhKXtyZXR1cm4gYS5ub2RlVHlwZT09PU5vZGUuRUxFTUVOVF9OT0RFJiYibGluayI9PT1hLmxvY2FsTmFtZSYmImltcG9ydCI9PT1hLnJlbH1mdW5jdGlvbiBrKCl7dmFyIGE9dGhpczt0aGlzLmE9e307dGhpcy5iPTA7dGhpcy5jPW5ldyBNdXRhdGlvbk9ic2VydmVyKGZ1bmN0aW9uKGIpe3JldHVybiBhLkphKGIpfSk7dGhpcy5jLm9ic2VydmUoZG9jdW1lbnQuaGVhZCx7Y2hpbGRMaXN0OiEwLHN1YnRyZWU6ITB9KTt0aGlzLmxvYWRJbXBvcnRzKGRvY3VtZW50KX1mdW5jdGlvbiBsKGEpe3EobShhLCJ0ZW1wbGF0ZSIpLGZ1bmN0aW9uKGEpe3EobShhLmNvbnRlbnQsJ3NjcmlwdDpub3QoW3R5cGVdKSxzY3JpcHRbdHlwZT0iYXBwbGljYXRpb24vamF2YXNjcmlwdCJdLHNjcmlwdFt0eXBlPSJ0ZXh0L2phdmFzY3JpcHQiXSxzY3JpcHRbdHlwZT0ibW9kdWxlIl0nKSwKZnVuY3Rpb24oYSl7dmFyIGI9ZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgic2NyaXB0Iik7cShhLmF0dHJpYnV0ZXMsZnVuY3Rpb24oYSl7cmV0dXJuIGIuc2V0QXR0cmlidXRlKGEubmFtZSxhLnZhbHVlKX0pO2IudGV4dENvbnRlbnQ9YS50ZXh0Q29udGVudDthLnBhcmVudE5vZGUucmVwbGFjZUNoaWxkKGIsYSl9KTtsKGEuY29udGVudCl9KX1mdW5jdGlvbiBtKGEsYil7cmV0dXJuIGEuY2hpbGROb2Rlcy5sZW5ndGg/YS5xdWVyeVNlbGVjdG9yQWxsKGIpOlV9ZnVuY3Rpb24gcShhLGIsYyl7dmFyIGQ9YT9hLmxlbmd0aDowLGU9Yz8tMToxO2ZvcihjPWM/ZC0xOjA7YzxkJiYwPD1jO2MrPWUpYihhW2NdLGMpfXZhciB4PWRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoImxpbmsiKSxNPSJpbXBvcnQiaW4geCxVPXgucXVlcnlTZWxlY3RvckFsbCgiKiIpLHVhPW51bGw7ITE9PT0iY3VycmVudFNjcmlwdCJpbiBkb2N1bWVudCYmT2JqZWN0LmRlZmluZVByb3BlcnR5KGRvY3VtZW50LCJjdXJyZW50U2NyaXB0IiwKe2dldDpmdW5jdGlvbigpe3JldHVybiB1YXx8KCJjb21wbGV0ZSIhPT1kb2N1bWVudC5yZWFkeVN0YXRlP2RvY3VtZW50LnNjcmlwdHNbZG9jdW1lbnQuc2NyaXB0cy5sZW5ndGgtMV06bnVsbCl9LGNvbmZpZ3VyYWJsZTohMH0pO3ZhciBsYj0vKHVybFwoKShbXildKikoXCkpL2csbWI9LyhAaW1wb3J0W1xzXSsoPyF1cmxcKCkpKFteO10qKSg7KS9nLFM9Lyg8bGlua1tePl0qKShyZWw9Wyd8Il0/c3R5bGVzaGVldFsnfCJdP1tePl0qPikvZyxDPXtFYTpmdW5jdGlvbihhLGIpe2EuaHJlZiYmYS5zZXRBdHRyaWJ1dGUoImhyZWYiLEMuWChhLmdldEF0dHJpYnV0ZSgiaHJlZiIpLGIpKTthLnNyYyYmYS5zZXRBdHRyaWJ1dGUoInNyYyIsQy5YKGEuZ2V0QXR0cmlidXRlKCJzcmMiKSxiKSk7aWYoInN0eWxlIj09PWEubG9jYWxOYW1lKXt2YXIgYz1DLnFhKGEudGV4dENvbnRlbnQsYixsYik7YS50ZXh0Q29udGVudD1DLnFhKGMsYixtYil9fSxxYTpmdW5jdGlvbihhLGIsYyl7cmV0dXJuIGEucmVwbGFjZShjLApmdW5jdGlvbihhLGMsZCxlKXthPWQucmVwbGFjZSgvWyInXS9nLCIiKTtiJiYoYT1DLlgoYSxiKSk7cmV0dXJuIGMrIiciK2ErIiciK2V9KX0sWDpmdW5jdGlvbihhLGIpe2lmKHZvaWQgMD09PUMuYWEpe0MuYWE9ITE7dHJ5e3ZhciBjPW5ldyBVUkwoImIiLCJodHRwOi8vYSIpO2MucGF0aG5hbWU9ImMlMjBkIjtDLmFhPSJodHRwOi8vYS9jJTIwZCI9PT1jLmhyZWZ9Y2F0Y2goeWMpe319aWYoQy5hYSlyZXR1cm4obmV3IFVSTChhLGIpKS5ocmVmO2M9Qy54YTtjfHwoYz1kb2N1bWVudC5pbXBsZW1lbnRhdGlvbi5jcmVhdGVIVE1MRG9jdW1lbnQoInRlbXAiKSxDLnhhPWMsYy5qYT1jLmNyZWF0ZUVsZW1lbnQoImJhc2UiKSxjLmhlYWQuYXBwZW5kQ2hpbGQoYy5qYSksYy5pYT1jLmNyZWF0ZUVsZW1lbnQoImEiKSk7Yy5qYS5ocmVmPWI7Yy5pYS5ocmVmPWE7cmV0dXJuIGMuaWEuaHJlZnx8YX19LFY9e2FzeW5jOiEwLGxvYWQ6ZnVuY3Rpb24oYSxiLGMpe2lmKGEpaWYoYS5tYXRjaCgvXmRhdGE6Lykpe2E9CmEuc3BsaXQoIiwiKTt2YXIgZD1hWzFdO2Q9LTE8YVswXS5pbmRleE9mKCI7YmFzZTY0Iik/YXRvYihkKTpkZWNvZGVVUklDb21wb25lbnQoZCk7YihkKX1lbHNle3ZhciBlPW5ldyBYTUxIdHRwUmVxdWVzdDtlLm9wZW4oIkdFVCIsYSxWLmFzeW5jKTtlLm9ubG9hZD1mdW5jdGlvbigpe3ZhciBhPWUucmVzcG9uc2VVUkx8fGUuZ2V0UmVzcG9uc2VIZWFkZXIoIkxvY2F0aW9uIik7YSYmMD09PWEuaW5kZXhPZigiLyIpJiYoYT0obG9jYXRpb24ub3JpZ2lufHxsb2NhdGlvbi5wcm90b2NvbCsiLy8iK2xvY2F0aW9uLmhvc3QpK2EpO3ZhciBkPWUucmVzcG9uc2V8fGUucmVzcG9uc2VUZXh0OzMwND09PWUuc3RhdHVzfHwwPT09ZS5zdGF0dXN8fDIwMDw9ZS5zdGF0dXMmJjMwMD5lLnN0YXR1cz9iKGQsYSk6YyhkKX07ZS5zZW5kKCl9ZWxzZSBjKCJlcnJvcjogaHJlZiBtdXN0IGJlIHNwZWNpZmllZCIpfX0saGE9L1RyaWRlbnQvLnRlc3QobmF2aWdhdG9yLnVzZXJBZ2VudCl8fC9FZGdlXC9cZC4vaS50ZXN0KG5hdmlnYXRvci51c2VyQWdlbnQpOwprLnByb3RvdHlwZS5sb2FkSW1wb3J0cz1mdW5jdGlvbihhKXt2YXIgYj10aGlzO2E9bShhLCJsaW5rW3JlbD1pbXBvcnRdIik7cShhLGZ1bmN0aW9uKGEpe3JldHVybiBiLmcoYSl9KX07ay5wcm90b3R5cGUuZz1mdW5jdGlvbihhKXt2YXIgYj10aGlzLGM9YS5ocmVmO2lmKHZvaWQgMCE9PXRoaXMuYVtjXSl7dmFyIGQ9dGhpcy5hW2NdO2QmJmQuX19sb2FkZWQmJihhLl9faW1wb3J0PWQsdGhpcy5mKGEpKX1lbHNlIHRoaXMuYisrLHRoaXMuYVtjXT0icGVuZGluZyIsVi5sb2FkKGMsZnVuY3Rpb24oYSxkKXthPWIuS2EoYSxkfHxjKTtiLmFbY109YTtiLmItLTtiLmxvYWRJbXBvcnRzKGEpO2IubCgpfSxmdW5jdGlvbigpe2IuYVtjXT1udWxsO2IuYi0tO2IubCgpfSl9O2sucHJvdG90eXBlLkthPWZ1bmN0aW9uKGEsYil7aWYoIWEpcmV0dXJuIGRvY3VtZW50LmNyZWF0ZURvY3VtZW50RnJhZ21lbnQoKTtoYSYmKGE9YS5yZXBsYWNlKFMsZnVuY3Rpb24oYSxiLGMpe3JldHVybi0xPT09CmEuaW5kZXhPZigidHlwZT0iKT9iKyIgdHlwZT1pbXBvcnQtZGlzYWJsZSAiK2M6YX0pKTt2YXIgYz1kb2N1bWVudC5jcmVhdGVFbGVtZW50KCJ0ZW1wbGF0ZSIpO2MuaW5uZXJIVE1MPWE7aWYoYy5jb250ZW50KWE9Yy5jb250ZW50LGwoYSk7ZWxzZSBmb3IoYT1kb2N1bWVudC5jcmVhdGVEb2N1bWVudEZyYWdtZW50KCk7Yy5maXJzdENoaWxkOylhLmFwcGVuZENoaWxkKGMuZmlyc3RDaGlsZCk7aWYoYz1hLnF1ZXJ5U2VsZWN0b3IoImJhc2UiKSliPUMuWChjLmdldEF0dHJpYnV0ZSgiaHJlZiIpLGIpLGMucmVtb3ZlQXR0cmlidXRlKCJocmVmIik7Yz1tKGEsJ2xpbmtbcmVsPWltcG9ydF0sbGlua1tyZWw9c3R5bGVzaGVldF1baHJlZl1bdHlwZT1pbXBvcnQtZGlzYWJsZV0sc3R5bGU6bm90KFt0eXBlXSksbGlua1tyZWw9c3R5bGVzaGVldF1baHJlZl06bm90KFt0eXBlXSksc2NyaXB0Om5vdChbdHlwZV0pLHNjcmlwdFt0eXBlPSJhcHBsaWNhdGlvbi9qYXZhc2NyaXB0Il0sc2NyaXB0W3R5cGU9InRleHQvamF2YXNjcmlwdCJdLHNjcmlwdFt0eXBlPSJtb2R1bGUiXScpOwp2YXIgZD0wO3EoYyxmdW5jdGlvbihhKXtnKGEpO0MuRWEoYSxiKTthLnNldEF0dHJpYnV0ZSgiaW1wb3J0LWRlcGVuZGVuY3kiLCIiKTtpZigic2NyaXB0Ij09PWEubG9jYWxOYW1lJiYhYS5zcmMmJmEudGV4dENvbnRlbnQpe2lmKCJtb2R1bGUiPT09YS50eXBlKXRocm93IEVycm9yKCJJbmxpbmUgbW9kdWxlIHNjcmlwdHMgYXJlIG5vdCBzdXBwb3J0ZWQgaW4gSFRNTCBJbXBvcnRzLiIpO2Euc2V0QXR0cmlidXRlKCJzcmMiLCJkYXRhOnRleHQvamF2YXNjcmlwdDtjaGFyc2V0PXV0Zi04LCIrZW5jb2RlVVJJQ29tcG9uZW50KGEudGV4dENvbnRlbnQrKCJcbi8vIyBzb3VyY2VVUkw9IitiKyhkPyItIitkOiIiKSsiLmpzXG4iKSkpO2EudGV4dENvbnRlbnQ9IiI7ZCsrfX0pO3JldHVybiBhfTtrLnByb3RvdHlwZS5sPWZ1bmN0aW9uKCl7dmFyIGE9dGhpcztpZighdGhpcy5iKXt0aGlzLmMuZGlzY29ubmVjdCgpO3RoaXMuZmxhdHRlbihkb2N1bWVudCk7dmFyIGI9ITEsYz0hMSxkPWZ1bmN0aW9uKCl7YyYmCmImJihhLmxvYWRJbXBvcnRzKGRvY3VtZW50KSxhLmJ8fChhLmMub2JzZXJ2ZShkb2N1bWVudC5oZWFkLHtjaGlsZExpc3Q6ITAsc3VidHJlZTohMH0pLGEuZGEoKSkpfTt0aGlzLk1hKGZ1bmN0aW9uKCl7Yz0hMDtkKCl9KTt0aGlzLkxhKGZ1bmN0aW9uKCl7Yj0hMDtkKCl9KX19O2sucHJvdG90eXBlLmZsYXR0ZW49ZnVuY3Rpb24oYSl7dmFyIGI9dGhpczthPW0oYSwibGlua1tyZWw9aW1wb3J0XSIpO3EoYSxmdW5jdGlvbihhKXt2YXIgYz1iLmFbYS5ocmVmXTsoYS5fX2ltcG9ydD1jKSYmYy5ub2RlVHlwZT09PU5vZGUuRE9DVU1FTlRfRlJBR01FTlRfTk9ERSYmKGIuYVthLmhyZWZdPWEsYS5yZWFkeVN0YXRlPSJsb2FkaW5nIixhLl9faW1wb3J0PWEsYi5mbGF0dGVuKGMpLGEuYXBwZW5kQ2hpbGQoYykpfSl9O2sucHJvdG90eXBlLkxhPWZ1bmN0aW9uKGEpe2Z1bmN0aW9uIGIoZSl7aWYoZTxkKXt2YXIgZj1jW2VdLGg9ZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgic2NyaXB0Iik7Zi5yZW1vdmVBdHRyaWJ1dGUoImltcG9ydC1kZXBlbmRlbmN5Iik7CnEoZi5hdHRyaWJ1dGVzLGZ1bmN0aW9uKGEpe3JldHVybiBoLnNldEF0dHJpYnV0ZShhLm5hbWUsYS52YWx1ZSl9KTt1YT1oO2YucGFyZW50Tm9kZS5yZXBsYWNlQ2hpbGQoaCxmKTtnKGgsZnVuY3Rpb24oKXt1YT1udWxsO2IoZSsxKX0pfWVsc2UgYSgpfXZhciBjPW0oZG9jdW1lbnQsInNjcmlwdFtpbXBvcnQtZGVwZW5kZW5jeV0iKSxkPWMubGVuZ3RoO2IoMCl9O2sucHJvdG90eXBlLk1hPWZ1bmN0aW9uKGEpe3ZhciBiPW0oZG9jdW1lbnQsInN0eWxlW2ltcG9ydC1kZXBlbmRlbmN5XSxsaW5rW3JlbD1zdHlsZXNoZWV0XVtpbXBvcnQtZGVwZW5kZW5jeV0iKSxkPWIubGVuZ3RoO2lmKGQpe3ZhciBlPWhhJiYhIWRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoImxpbmtbcmVsPXN0eWxlc2hlZXRdW2hyZWZdW3R5cGU9aW1wb3J0LWRpc2FibGVdIik7cShiLGZ1bmN0aW9uKGIpe2coYixmdW5jdGlvbigpe2IucmVtb3ZlQXR0cmlidXRlKCJpbXBvcnQtZGVwZW5kZW5jeSIpOzA9PT0tLWQmJgphKCl9KTtpZihlJiZiLnBhcmVudE5vZGUhPT1kb2N1bWVudC5oZWFkKXt2YXIgZj1kb2N1bWVudC5jcmVhdGVFbGVtZW50KGIubG9jYWxOYW1lKTtmLl9fYXBwbGllZEVsZW1lbnQ9YjtmLnNldEF0dHJpYnV0ZSgidHlwZSIsImltcG9ydC1wbGFjZWhvbGRlciIpO2IucGFyZW50Tm9kZS5pbnNlcnRCZWZvcmUoZixiLm5leHRTaWJsaW5nKTtmb3IoZj1jKGIpO2YmJmMoZik7KWY9YyhmKTtmLnBhcmVudE5vZGUhPT1kb2N1bWVudC5oZWFkJiYoZj1udWxsKTtkb2N1bWVudC5oZWFkLmluc2VydEJlZm9yZShiLGYpO2IucmVtb3ZlQXR0cmlidXRlKCJ0eXBlIil9fSl9ZWxzZSBhKCl9O2sucHJvdG90eXBlLmRhPWZ1bmN0aW9uKCl7dmFyIGE9dGhpcyxiPW0oZG9jdW1lbnQsImxpbmtbcmVsPWltcG9ydF0iKTtxKGIsZnVuY3Rpb24oYil7cmV0dXJuIGEuZihiKX0sITApfTtrLnByb3RvdHlwZS5mPWZ1bmN0aW9uKGEpe2EuX19sb2FkZWR8fChhLl9fbG9hZGVkPSEwLGEuaW1wb3J0JiYoYS5pbXBvcnQucmVhZHlTdGF0ZT0KImNvbXBsZXRlIiksYS5kaXNwYXRjaEV2ZW50KGIoYS5pbXBvcnQ/ImxvYWQiOiJlcnJvciIse2J1YmJsZXM6ITEsY2FuY2VsYWJsZTohMSxkZXRhaWw6dm9pZCAwfSkpKX07ay5wcm90b3R5cGUuSmE9ZnVuY3Rpb24oYSl7dmFyIGI9dGhpcztxKGEsZnVuY3Rpb24oYSl7cmV0dXJuIHEoYS5hZGRlZE5vZGVzLGZ1bmN0aW9uKGEpe2EmJmEubm9kZVR5cGU9PT1Ob2RlLkVMRU1FTlRfTk9ERSYmKGgoYSk/Yi5nKGEpOmIubG9hZEltcG9ydHMoYSkpfSl9KX07dmFyIHZhPW51bGw7aWYoTSl4PW0oZG9jdW1lbnQsImxpbmtbcmVsPWltcG9ydF0iKSxxKHgsZnVuY3Rpb24oYSl7YS5pbXBvcnQmJiJsb2FkaW5nIj09PWEuaW1wb3J0LnJlYWR5U3RhdGV8fChhLl9fbG9hZGVkPSEwKX0pLHg9ZnVuY3Rpb24oYSl7YT1hLnRhcmdldDtoKGEpJiYoYS5fX2xvYWRlZD0hMCl9LGRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoImxvYWQiLHgsITApLGRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoImVycm9yIiwKeCwhMCk7ZWxzZXt2YXIgWD1PYmplY3QuZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yKE5vZGUucHJvdG90eXBlLCJiYXNlVVJJIik7T2JqZWN0LmRlZmluZVByb3BlcnR5KCghWHx8WC5jb25maWd1cmFibGU/Tm9kZTpFbGVtZW50KS5wcm90b3R5cGUsImJhc2VVUkkiLHtnZXQ6ZnVuY3Rpb24oKXt2YXIgYT1oKHRoaXMpP3RoaXM6Yyh0aGlzKTtyZXR1cm4gYT9hLmhyZWY6WCYmWC5nZXQ/WC5nZXQuY2FsbCh0aGlzKTooZG9jdW1lbnQucXVlcnlTZWxlY3RvcigiYmFzZSIpfHx3aW5kb3cubG9jYXRpb24pLmhyZWZ9LGNvbmZpZ3VyYWJsZTohMCxlbnVtZXJhYmxlOiEwfSk7T2JqZWN0LmRlZmluZVByb3BlcnR5KEhUTUxMaW5rRWxlbWVudC5wcm90b3R5cGUsImltcG9ydCIse2dldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLl9faW1wb3J0fHxudWxsfSxjb25maWd1cmFibGU6ITAsZW51bWVyYWJsZTohMH0pO2UoZnVuY3Rpb24oKXt2YT1uZXcga30pfWYoZnVuY3Rpb24oKXtyZXR1cm4gZG9jdW1lbnQuZGlzcGF0Y2hFdmVudChiKCJIVE1MSW1wb3J0c0xvYWRlZCIsCntjYW5jZWxhYmxlOiEwLGJ1YmJsZXM6ITAsZGV0YWlsOnZvaWQgMH0pKX0pO2EudXNlTmF0aXZlPU07YS53aGVuUmVhZHk9ZjthLmltcG9ydEZvckVsZW1lbnQ9YzthLmxvYWRJbXBvcnRzPWZ1bmN0aW9uKGEpe3ZhJiZ2YS5sb2FkSW1wb3J0cyhhKX19KSh3aW5kb3cuSFRNTEltcG9ydHM9d2luZG93LkhUTUxJbXBvcnRzfHx7fSk7LyoKCiBDb3B5cmlnaHQgKGMpIDIwMTQgVGhlIFBvbHltZXIgUHJvamVjdCBBdXRob3JzLiBBbGwgcmlnaHRzIHJlc2VydmVkLgogVGhpcyBjb2RlIG1heSBvbmx5IGJlIHVzZWQgdW5kZXIgdGhlIEJTRCBzdHlsZSBsaWNlbnNlIGZvdW5kIGF0IGh0dHA6Ly9wb2x5bWVyLmdpdGh1Yi5pby9MSUNFTlNFLnR4dAogVGhlIGNvbXBsZXRlIHNldCBvZiBhdXRob3JzIG1heSBiZSBmb3VuZCBhdCBodHRwOi8vcG9seW1lci5naXRodWIuaW8vQVVUSE9SUy50eHQKIFRoZSBjb21wbGV0ZSBzZXQgb2YgY29udHJpYnV0b3JzIG1heSBiZSBmb3VuZCBhdCBodHRwOi8vcG9seW1lci5naXRodWIuaW8vQ09OVFJJQlVUT1JTLnR4dAogQ29kZSBkaXN0cmlidXRlZCBieSBHb29nbGUgYXMgcGFydCBvZiB0aGUgcG9seW1lciBwcm9qZWN0IGlzIGFsc28KIHN1YmplY3QgdG8gYW4gYWRkaXRpb25hbCBJUCByaWdodHMgZ3JhbnQgZm91bmQgYXQgaHR0cDovL3BvbHltZXIuZ2l0aHViLmlvL1BBVEVOVFMudHh0CiovCndpbmRvdy5XZWJDb21wb25lbnRzPXdpbmRvdy5XZWJDb21wb25lbnRzfHx7ZmxhZ3M6e319O3ZhciBDYT1kb2N1bWVudC5xdWVyeVNlbGVjdG9yKCdzY3JpcHRbc3JjKj0id2ViY29tcG9uZW50cy1saXRlLmpzIl0nKSxEYT0vd2MtKC4rKS8sdD17fTtpZighdC5ub09wdHMpe2xvY2F0aW9uLnNlYXJjaC5zbGljZSgxKS5zcGxpdCgiJiIpLmZvckVhY2goZnVuY3Rpb24oYSl7YT1hLnNwbGl0KCI9Iik7dmFyIGI7YVswXSYmKGI9YVswXS5tYXRjaChEYSkpJiYodFtiWzFdXT1hWzFdfHwhMCl9KTtpZihDYSlmb3IodmFyIEVhPTAsRmE9dm9pZCAwO0ZhPUNhLmF0dHJpYnV0ZXNbRWFdO0VhKyspInNyYyIhPT1GYS5uYW1lJiYodFtGYS5uYW1lXT1GYS52YWx1ZXx8ITApO2lmKHQubG9nJiZ0LmxvZy5zcGxpdCl7dmFyIEdhPXQubG9nLnNwbGl0KCIsIik7dC5sb2c9e307R2EuZm9yRWFjaChmdW5jdGlvbihhKXt0LmxvZ1thXT0hMH0pfWVsc2UgdC5sb2c9e319CndpbmRvdy5XZWJDb21wb25lbnRzLmZsYWdzPXQ7dmFyIEhhPXQuc2hhZHlkb207SGEmJih3aW5kb3cuU2hhZHlET009d2luZG93LlNoYWR5RE9NfHx7fSx3aW5kb3cuU2hhZHlET00uZm9yY2U9SGEpO3ZhciBJYT10LnJlZ2lzdGVyfHx0LmNlO0lhJiZ3aW5kb3cuY3VzdG9tRWxlbWVudHMmJih3aW5kb3cuY3VzdG9tRWxlbWVudHMuZm9yY2VQb2x5ZmlsbD1JYSk7LyoKCkNvcHlyaWdodCAoYykgMjAxNiBUaGUgUG9seW1lciBQcm9qZWN0IEF1dGhvcnMuIEFsbCByaWdodHMgcmVzZXJ2ZWQuClRoaXMgY29kZSBtYXkgb25seSBiZSB1c2VkIHVuZGVyIHRoZSBCU0Qgc3R5bGUgbGljZW5zZSBmb3VuZCBhdCBodHRwOi8vcG9seW1lci5naXRodWIuaW8vTElDRU5TRS50eHQKVGhlIGNvbXBsZXRlIHNldCBvZiBhdXRob3JzIG1heSBiZSBmb3VuZCBhdCBodHRwOi8vcG9seW1lci5naXRodWIuaW8vQVVUSE9SUy50eHQKVGhlIGNvbXBsZXRlIHNldCBvZiBjb250cmlidXRvcnMgbWF5IGJlIGZvdW5kIGF0IGh0dHA6Ly9wb2x5bWVyLmdpdGh1Yi5pby9DT05UUklCVVRPUlMudHh0CkNvZGUgZGlzdHJpYnV0ZWQgYnkgR29vZ2xlIGFzIHBhcnQgb2YgdGhlIHBvbHltZXIgcHJvamVjdCBpcyBhbHNvCnN1YmplY3QgdG8gYW4gYWRkaXRpb25hbCBJUCByaWdodHMgZ3JhbnQgZm91bmQgYXQgaHR0cDovL3BvbHltZXIuZ2l0aHViLmlvL1BBVEVOVFMudHh0CiovCmZ1bmN0aW9uIEphKCl7dGhpcy5wYT10aGlzLnJvb3Q9bnVsbDt0aGlzLlQ9ITE7dGhpcy5EPXRoaXMuUD10aGlzLmNhPXRoaXMuYXNzaWduZWRTbG90PXRoaXMuYXNzaWduZWROb2Rlcz10aGlzLkg9bnVsbDt0aGlzLmNoaWxkTm9kZXM9dGhpcy5uZXh0U2libGluZz10aGlzLnByZXZpb3VzU2libGluZz10aGlzLmxhc3RDaGlsZD10aGlzLmZpcnN0Q2hpbGQ9dGhpcy5wYXJlbnROb2RlPXRoaXMuSz12b2lkIDA7dGhpcy5rYT10aGlzLmxhPSExO3RoaXMuTz17fX1KYS5wcm90b3R5cGUudG9KU09OPWZ1bmN0aW9uKCl7cmV0dXJue319O2Z1bmN0aW9uIHUoYSl7YS5fX3NoYWR5fHwoYS5fX3NoYWR5PW5ldyBKYSk7cmV0dXJuIGEuX19zaGFkeX1mdW5jdGlvbiB2KGEpe3JldHVybiBhJiZhLl9fc2hhZHl9O3ZhciB3PXdpbmRvdy5TaGFkeURPTXx8e307dy5HYT0hKCFFbGVtZW50LnByb3RvdHlwZS5hdHRhY2hTaGFkb3d8fCFOb2RlLnByb3RvdHlwZS5nZXRSb290Tm9kZSk7dmFyIEthPU9iamVjdC5nZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3IoTm9kZS5wcm90b3R5cGUsImZpcnN0Q2hpbGQiKTt3Lm09ISEoS2EmJkthLmNvbmZpZ3VyYWJsZSYmS2EuZ2V0KTt3LmVhPXcuZm9yY2V8fCF3LkdhO3cuSj13Lm5vUGF0Y2h8fCExO3cub2E9dy5wcmVmZXJQZXJmb3JtYW5jZTtmdW5jdGlvbiB5KGEpe3JldHVybihhPXYoYSkpJiZ2b2lkIDAhPT1hLmZpcnN0Q2hpbGR9ZnVuY3Rpb24geihhKXtyZXR1cm4iU2hhZHlSb290Ij09PWEuemF9ZnVuY3Rpb24gTGEoYSl7cmV0dXJuKGE9KGE9dihhKSkmJmEucm9vdCkmJk1hKGEpfQp2YXIgTmE9RWxlbWVudC5wcm90b3R5cGUsT2E9TmEubWF0Y2hlc3x8TmEubWF0Y2hlc1NlbGVjdG9yfHxOYS5tb3pNYXRjaGVzU2VsZWN0b3J8fE5hLm1zTWF0Y2hlc1NlbGVjdG9yfHxOYS5vTWF0Y2hlc1NlbGVjdG9yfHxOYS53ZWJraXRNYXRjaGVzU2VsZWN0b3IsUGE9ZG9jdW1lbnQuY3JlYXRlVGV4dE5vZGUoIiIpLFFhPTAsUmE9W107KG5ldyBNdXRhdGlvbk9ic2VydmVyKGZ1bmN0aW9uKCl7Zm9yKDtSYS5sZW5ndGg7KXRyeXtSYS5zaGlmdCgpKCl9Y2F0Y2goYSl7dGhyb3cgUGEudGV4dENvbnRlbnQ9UWErKyxhO319KSkub2JzZXJ2ZShQYSx7Y2hhcmFjdGVyRGF0YTohMH0pO2Z1bmN0aW9uIFNhKGEpe1JhLnB1c2goYSk7UGEudGV4dENvbnRlbnQ9UWErK312YXIgVGE9ISFkb2N1bWVudC5jb250YWlucztmdW5jdGlvbiBVYShhLGIpe2Zvcig7Yjspe2lmKGI9PWEpcmV0dXJuITA7Yj1iLl9fc2hhZHlfcGFyZW50Tm9kZX1yZXR1cm4hMX0KZnVuY3Rpb24gVmEoYSl7Zm9yKHZhciBiPWEubGVuZ3RoLTE7MDw9YjtiLS0pe3ZhciBjPWFbYl0sZD1jLmdldEF0dHJpYnV0ZSgiaWQiKXx8Yy5nZXRBdHRyaWJ1dGUoIm5hbWUiKTtkJiYibGVuZ3RoIiE9PWQmJmlzTmFOKGQpJiYoYVtkXT1jKX1hLml0ZW09ZnVuY3Rpb24oYil7cmV0dXJuIGFbYl19O2EubmFtZWRJdGVtPWZ1bmN0aW9uKGIpe2lmKCJsZW5ndGgiIT09YiYmaXNOYU4oYikmJmFbYl0pcmV0dXJuIGFbYl07Zm9yKHZhciBjPWlhKGEpLGQ9Yy5uZXh0KCk7IWQuZG9uZTtkPWMubmV4dCgpKWlmKGQ9ZC52YWx1ZSwoZC5nZXRBdHRyaWJ1dGUoImlkIil8fGQuZ2V0QXR0cmlidXRlKCJuYW1lIikpPT1iKXJldHVybiBkO3JldHVybiBudWxsfTtyZXR1cm4gYX0KZnVuY3Rpb24gQShhLGIsYyxkKXtjPXZvaWQgMD09PWM/IiI6Yztmb3IodmFyIGUgaW4gYil7dmFyIGY9YltlXTtpZighKGQmJjA8PWQuaW5kZXhPZihlKSkpe2YuY29uZmlndXJhYmxlPSEwO3ZhciBnPWMrZTtpZihmLnZhbHVlKWFbZ109Zi52YWx1ZTtlbHNlIHRyeXtPYmplY3QuZGVmaW5lUHJvcGVydHkoYSxnLGYpfWNhdGNoKGgpe319fX1mdW5jdGlvbiBCKGEpe3ZhciBiPXt9O09iamVjdC5nZXRPd25Qcm9wZXJ0eU5hbWVzKGEpLmZvckVhY2goZnVuY3Rpb24oYyl7YltjXT1PYmplY3QuZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yKGEsYyl9KTtyZXR1cm4gYn07dmFyIFdhPVtdLFhhO2Z1bmN0aW9uIFlhKGEpe1hhfHwoWGE9ITAsU2EoWmEpKTtXYS5wdXNoKGEpfWZ1bmN0aW9uIFphKCl7WGE9ITE7Zm9yKHZhciBhPSEhV2EubGVuZ3RoO1dhLmxlbmd0aDspV2Euc2hpZnQoKSgpO3JldHVybiBhfVphLmxpc3Q9V2E7ZnVuY3Rpb24gJGEoKXt0aGlzLmE9ITE7dGhpcy5hZGRlZE5vZGVzPVtdO3RoaXMucmVtb3ZlZE5vZGVzPVtdO3RoaXMuUz1uZXcgU2V0fWZ1bmN0aW9uIGFiKGEpe2EuYXx8KGEuYT0hMCxTYShmdW5jdGlvbigpe2EuZmx1c2goKX0pKX0kYS5wcm90b3R5cGUuZmx1c2g9ZnVuY3Rpb24oKXtpZih0aGlzLmEpe3RoaXMuYT0hMTt2YXIgYT10aGlzLnRha2VSZWNvcmRzKCk7YS5sZW5ndGgmJnRoaXMuUy5mb3JFYWNoKGZ1bmN0aW9uKGIpe2IoYSl9KX19OyRhLnByb3RvdHlwZS50YWtlUmVjb3Jkcz1mdW5jdGlvbigpe2lmKHRoaXMuYWRkZWROb2Rlcy5sZW5ndGh8fHRoaXMucmVtb3ZlZE5vZGVzLmxlbmd0aCl7dmFyIGE9W3thZGRlZE5vZGVzOnRoaXMuYWRkZWROb2RlcyxyZW1vdmVkTm9kZXM6dGhpcy5yZW1vdmVkTm9kZXN9XTt0aGlzLmFkZGVkTm9kZXM9W107dGhpcy5yZW1vdmVkTm9kZXM9W107cmV0dXJuIGF9cmV0dXJuW119OwpmdW5jdGlvbiBiYihhLGIpe3ZhciBjPXUoYSk7Yy5IfHwoYy5IPW5ldyAkYSk7Yy5ILlMuYWRkKGIpO3ZhciBkPWMuSDtyZXR1cm57eWE6YixGOmQsQWE6YSx0YWtlUmVjb3JkczpmdW5jdGlvbigpe3JldHVybiBkLnRha2VSZWNvcmRzKCl9fX1mdW5jdGlvbiBjYihhKXt2YXIgYj1hJiZhLkY7YiYmKGIuUy5kZWxldGUoYS55YSksYi5TLnNpemV8fCh1KGEuQWEpLkg9bnVsbCkpfQpmdW5jdGlvbiBkYihhLGIpe3ZhciBjPWIuZ2V0Um9vdE5vZGUoKTtyZXR1cm4gYS5tYXAoZnVuY3Rpb24oYSl7dmFyIGI9Yz09PWEudGFyZ2V0LmdldFJvb3ROb2RlKCk7aWYoYiYmYS5hZGRlZE5vZGVzKXtpZihiPUFycmF5LmZyb20oYS5hZGRlZE5vZGVzKS5maWx0ZXIoZnVuY3Rpb24oYSl7cmV0dXJuIGM9PT1hLmdldFJvb3ROb2RlKCl9KSxiLmxlbmd0aClyZXR1cm4gYT1PYmplY3QuY3JlYXRlKGEpLE9iamVjdC5kZWZpbmVQcm9wZXJ0eShhLCJhZGRlZE5vZGVzIix7dmFsdWU6Yixjb25maWd1cmFibGU6ITB9KSxhfWVsc2UgaWYoYilyZXR1cm4gYX0pLmZpbHRlcihmdW5jdGlvbihhKXtyZXR1cm4gYX0pfTt2YXIgZWI9L1smXHUwMEEwIl0vZyxmYj0vWyZcdTAwQTA8Pl0vZztmdW5jdGlvbiBnYihhKXtzd2l0Y2goYSl7Y2FzZSAiJiI6cmV0dXJuIiZhbXA7IjtjYXNlICI8IjpyZXR1cm4iJmx0OyI7Y2FzZSAiPiI6cmV0dXJuIiZndDsiO2Nhc2UgJyInOnJldHVybiImcXVvdDsiO2Nhc2UgIlx1MDBhMCI6cmV0dXJuIiZuYnNwOyJ9fWZ1bmN0aW9uIGhiKGEpe2Zvcih2YXIgYj17fSxjPTA7YzxhLmxlbmd0aDtjKyspYlthW2NdXT0hMDtyZXR1cm4gYn12YXIgaWI9aGIoImFyZWEgYmFzZSBiciBjb2wgY29tbWFuZCBlbWJlZCBociBpbWcgaW5wdXQga2V5Z2VuIGxpbmsgbWV0YSBwYXJhbSBzb3VyY2UgdHJhY2sgd2JyIi5zcGxpdCgiICIpKSxqYj1oYigic3R5bGUgc2NyaXB0IHhtcCBpZnJhbWUgbm9lbWJlZCBub2ZyYW1lcyBwbGFpbnRleHQgbm9zY3JpcHQiLnNwbGl0KCIgIikpOwpmdW5jdGlvbiBrYihhLGIpeyJ0ZW1wbGF0ZSI9PT1hLmxvY2FsTmFtZSYmKGE9YS5jb250ZW50KTtmb3IodmFyIGM9IiIsZD1iP2IoYSk6YS5jaGlsZE5vZGVzLGU9MCxmPWQubGVuZ3RoLGc9dm9pZCAwO2U8ZiYmKGc9ZFtlXSk7ZSsrKXthOnt2YXIgaD1nO3ZhciBrPWEsbD1iO3N3aXRjaChoLm5vZGVUeXBlKXtjYXNlIE5vZGUuRUxFTUVOVF9OT0RFOms9aC5sb2NhbE5hbWU7Zm9yKHZhciBtPSI8IitrLHE9aC5hdHRyaWJ1dGVzLHg9MCxNO009cVt4XTt4KyspbSs9IiAiK00ubmFtZSsnPSInK00udmFsdWUucmVwbGFjZShlYixnYikrJyInO20rPSI+IjtoPWliW2tdP206bStrYihoLGwpKyI8LyIraysiPiI7YnJlYWsgYTtjYXNlIE5vZGUuVEVYVF9OT0RFOmg9aC5kYXRhO2g9ayYmamJbay5sb2NhbE5hbWVdP2g6aC5yZXBsYWNlKGZiLGdiKTticmVhayBhO2Nhc2UgTm9kZS5DT01NRU5UX05PREU6aD0iXHgzYyEtLSIraC5kYXRhKyItLVx4M2UiO2JyZWFrIGE7ZGVmYXVsdDp0aHJvdyB3aW5kb3cuY29uc29sZS5lcnJvcihoKSwKRXJyb3IoIm5vdCBpbXBsZW1lbnRlZCIpO319Yys9aH1yZXR1cm4gY307dmFyIHBiPXcubSxxYj17cXVlcnlTZWxlY3RvcjpmdW5jdGlvbihhKXtyZXR1cm4gdGhpcy5fX3NoYWR5X25hdGl2ZV9xdWVyeVNlbGVjdG9yKGEpfSxxdWVyeVNlbGVjdG9yQWxsOmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLl9fc2hhZHlfbmF0aXZlX3F1ZXJ5U2VsZWN0b3JBbGwoYSl9fSxyYj17fTtmdW5jdGlvbiBzYihhKXtyYlthXT1mdW5jdGlvbihiKXtyZXR1cm4gYlsiX19zaGFkeV9uYXRpdmVfIithXX19ZnVuY3Rpb24gdGIoYSxiKXtBKGEsYiwiX19zaGFkeV9uYXRpdmVfIik7Zm9yKHZhciBjIGluIGIpc2IoYyl9ZnVuY3Rpb24gRChhLGIpe2I9dm9pZCAwPT09Yj9bXTpiO2Zvcih2YXIgYz0wO2M8Yi5sZW5ndGg7YysrKXt2YXIgZD1iW2NdLGU9T2JqZWN0LmdldE93blByb3BlcnR5RGVzY3JpcHRvcihhLGQpO2UmJihPYmplY3QuZGVmaW5lUHJvcGVydHkoYSwiX19zaGFkeV9uYXRpdmVfIitkLGUpLGUudmFsdWU/cWJbZF18fChxYltkXT1lLnZhbHVlKTpzYihkKSl9fQp2YXIgRT1kb2N1bWVudC5jcmVhdGVUcmVlV2Fsa2VyKGRvY3VtZW50LE5vZGVGaWx0ZXIuU0hPV19BTEwsbnVsbCwhMSksRj1kb2N1bWVudC5jcmVhdGVUcmVlV2Fsa2VyKGRvY3VtZW50LE5vZGVGaWx0ZXIuU0hPV19FTEVNRU5ULG51bGwsITEpLHViPWRvY3VtZW50LmltcGxlbWVudGF0aW9uLmNyZWF0ZUhUTUxEb2N1bWVudCgiaW5lcnQiKTtmdW5jdGlvbiB2YihhKXtmb3IodmFyIGI7Yj1hLl9fc2hhZHlfbmF0aXZlX2ZpcnN0Q2hpbGQ7KWEuX19zaGFkeV9uYXRpdmVfcmVtb3ZlQ2hpbGQoYil9dmFyIHdiPVsiZmlyc3RFbGVtZW50Q2hpbGQiLCJsYXN0RWxlbWVudENoaWxkIiwiY2hpbGRyZW4iLCJjaGlsZEVsZW1lbnRDb3VudCJdLHhiPVsicXVlcnlTZWxlY3RvciIsInF1ZXJ5U2VsZWN0b3JBbGwiXTsKZnVuY3Rpb24geWIoKXt2YXIgYT1bImRpc3BhdGNoRXZlbnQiLCJhZGRFdmVudExpc3RlbmVyIiwicmVtb3ZlRXZlbnRMaXN0ZW5lciJdO3dpbmRvdy5FdmVudFRhcmdldD9EKHdpbmRvdy5FdmVudFRhcmdldC5wcm90b3R5cGUsYSk6KEQoTm9kZS5wcm90b3R5cGUsYSksRChXaW5kb3cucHJvdG90eXBlLGEpKTtwYj9EKE5vZGUucHJvdG90eXBlLCJwYXJlbnROb2RlIGZpcnN0Q2hpbGQgbGFzdENoaWxkIHByZXZpb3VzU2libGluZyBuZXh0U2libGluZyBjaGlsZE5vZGVzIHBhcmVudEVsZW1lbnQgdGV4dENvbnRlbnQiLnNwbGl0KCIgIikpOnRiKE5vZGUucHJvdG90eXBlLHtwYXJlbnROb2RlOntnZXQ6ZnVuY3Rpb24oKXtFLmN1cnJlbnROb2RlPXRoaXM7cmV0dXJuIEUucGFyZW50Tm9kZSgpfX0sZmlyc3RDaGlsZDp7Z2V0OmZ1bmN0aW9uKCl7RS5jdXJyZW50Tm9kZT10aGlzO3JldHVybiBFLmZpcnN0Q2hpbGQoKX19LGxhc3RDaGlsZDp7Z2V0OmZ1bmN0aW9uKCl7RS5jdXJyZW50Tm9kZT0KdGhpcztyZXR1cm4gRS5sYXN0Q2hpbGQoKX19LHByZXZpb3VzU2libGluZzp7Z2V0OmZ1bmN0aW9uKCl7RS5jdXJyZW50Tm9kZT10aGlzO3JldHVybiBFLnByZXZpb3VzU2libGluZygpfX0sbmV4dFNpYmxpbmc6e2dldDpmdW5jdGlvbigpe0UuY3VycmVudE5vZGU9dGhpcztyZXR1cm4gRS5uZXh0U2libGluZygpfX0sY2hpbGROb2Rlczp7Z2V0OmZ1bmN0aW9uKCl7dmFyIGE9W107RS5jdXJyZW50Tm9kZT10aGlzO2Zvcih2YXIgYz1FLmZpcnN0Q2hpbGQoKTtjOylhLnB1c2goYyksYz1FLm5leHRTaWJsaW5nKCk7cmV0dXJuIGF9fSxwYXJlbnRFbGVtZW50OntnZXQ6ZnVuY3Rpb24oKXtGLmN1cnJlbnROb2RlPXRoaXM7cmV0dXJuIEYucGFyZW50Tm9kZSgpfX0sdGV4dENvbnRlbnQ6e2dldDpmdW5jdGlvbigpe3N3aXRjaCh0aGlzLm5vZGVUeXBlKXtjYXNlIE5vZGUuRUxFTUVOVF9OT0RFOmNhc2UgTm9kZS5ET0NVTUVOVF9GUkFHTUVOVF9OT0RFOmZvcih2YXIgYT1kb2N1bWVudC5jcmVhdGVUcmVlV2Fsa2VyKHRoaXMsCk5vZGVGaWx0ZXIuU0hPV19URVhULG51bGwsITEpLGM9IiIsZDtkPWEubmV4dE5vZGUoKTspYys9ZC5ub2RlVmFsdWU7cmV0dXJuIGM7ZGVmYXVsdDpyZXR1cm4gdGhpcy5ub2RlVmFsdWV9fSxzZXQ6ZnVuY3Rpb24oYSl7aWYoInVuZGVmaW5lZCI9PT10eXBlb2YgYXx8bnVsbD09PWEpYT0iIjtzd2l0Y2godGhpcy5ub2RlVHlwZSl7Y2FzZSBOb2RlLkVMRU1FTlRfTk9ERTpjYXNlIE5vZGUuRE9DVU1FTlRfRlJBR01FTlRfTk9ERTp2Yih0aGlzKTsoMDxhLmxlbmd0aHx8dGhpcy5ub2RlVHlwZT09PU5vZGUuRUxFTUVOVF9OT0RFKSYmdGhpcy5fX3NoYWR5X25hdGl2ZV9pbnNlcnRCZWZvcmUoZG9jdW1lbnQuY3JlYXRlVGV4dE5vZGUoYSksdm9pZCAwKTticmVhaztkZWZhdWx0OnRoaXMubm9kZVZhbHVlPWF9fX19KTtEKE5vZGUucHJvdG90eXBlLCJhcHBlbmRDaGlsZCBpbnNlcnRCZWZvcmUgcmVtb3ZlQ2hpbGQgcmVwbGFjZUNoaWxkIGNsb25lTm9kZSBjb250YWlucyIuc3BsaXQoIiAiKSk7CmE9e2ZpcnN0RWxlbWVudENoaWxkOntnZXQ6ZnVuY3Rpb24oKXtGLmN1cnJlbnROb2RlPXRoaXM7cmV0dXJuIEYuZmlyc3RDaGlsZCgpfX0sbGFzdEVsZW1lbnRDaGlsZDp7Z2V0OmZ1bmN0aW9uKCl7Ri5jdXJyZW50Tm9kZT10aGlzO3JldHVybiBGLmxhc3RDaGlsZCgpfX0sY2hpbGRyZW46e2dldDpmdW5jdGlvbigpe3ZhciBhPVtdO0YuY3VycmVudE5vZGU9dGhpcztmb3IodmFyIGM9Ri5maXJzdENoaWxkKCk7YzspYS5wdXNoKGMpLGM9Ri5uZXh0U2libGluZygpO3JldHVybiBWYShhKX19LGNoaWxkRWxlbWVudENvdW50OntnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5jaGlsZHJlbj90aGlzLmNoaWxkcmVuLmxlbmd0aDowfX19O3BiPyhEKEVsZW1lbnQucHJvdG90eXBlLHdiKSxEKEVsZW1lbnQucHJvdG90eXBlLFsicHJldmlvdXNFbGVtZW50U2libGluZyIsIm5leHRFbGVtZW50U2libGluZyIsImlubmVySFRNTCJdKSxPYmplY3QuZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yKEhUTUxFbGVtZW50LnByb3RvdHlwZSwKImNoaWxkcmVuIikmJkQoSFRNTEVsZW1lbnQucHJvdG90eXBlLFsiY2hpbGRyZW4iXSksT2JqZWN0LmdldE93blByb3BlcnR5RGVzY3JpcHRvcihIVE1MRWxlbWVudC5wcm90b3R5cGUsImlubmVySFRNTCIpJiZEKEhUTUxFbGVtZW50LnByb3RvdHlwZSxbImlubmVySFRNTCJdKSk6KHRiKEVsZW1lbnQucHJvdG90eXBlLGEpLHRiKEVsZW1lbnQucHJvdG90eXBlLHtwcmV2aW91c0VsZW1lbnRTaWJsaW5nOntnZXQ6ZnVuY3Rpb24oKXtGLmN1cnJlbnROb2RlPXRoaXM7cmV0dXJuIEYucHJldmlvdXNTaWJsaW5nKCl9fSxuZXh0RWxlbWVudFNpYmxpbmc6e2dldDpmdW5jdGlvbigpe0YuY3VycmVudE5vZGU9dGhpcztyZXR1cm4gRi5uZXh0U2libGluZygpfX0saW5uZXJIVE1MOntnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4ga2IodGhpcyxmdW5jdGlvbihhKXtyZXR1cm4gYS5fX3NoYWR5X25hdGl2ZV9jaGlsZE5vZGVzfSl9LHNldDpmdW5jdGlvbihhKXt2YXIgYj0idGVtcGxhdGUiPT09dGhpcy5sb2NhbE5hbWU/CnRoaXMuY29udGVudDp0aGlzO3ZiKGIpO3ZhciBkPXRoaXMubG9jYWxOYW1lfHwiZGl2IjtkPXRoaXMubmFtZXNwYWNlVVJJJiZ0aGlzLm5hbWVzcGFjZVVSSSE9PXViLm5hbWVzcGFjZVVSST91Yi5jcmVhdGVFbGVtZW50TlModGhpcy5uYW1lc3BhY2VVUkksZCk6dWIuY3JlYXRlRWxlbWVudChkKTtkLmlubmVySFRNTD1hO2ZvcihhPSJ0ZW1wbGF0ZSI9PT10aGlzLmxvY2FsTmFtZT9kLmNvbnRlbnQ6ZDtkPWEuX19zaGFkeV9uYXRpdmVfZmlyc3RDaGlsZDspYi5fX3NoYWR5X25hdGl2ZV9pbnNlcnRCZWZvcmUoZCx2b2lkIDApfX19KSk7RChFbGVtZW50LnByb3RvdHlwZSwic2V0QXR0cmlidXRlIGdldEF0dHJpYnV0ZSBoYXNBdHRyaWJ1dGUgcmVtb3ZlQXR0cmlidXRlIGZvY3VzIGJsdXIiLnNwbGl0KCIgIikpO0QoRWxlbWVudC5wcm90b3R5cGUseGIpO0QoSFRNTEVsZW1lbnQucHJvdG90eXBlLFsiZm9jdXMiLCJibHVyIiwiY29udGFpbnMiXSk7cGImJkQoSFRNTEVsZW1lbnQucHJvdG90eXBlLApbInBhcmVudEVsZW1lbnQiLCJjaGlsZHJlbiIsImlubmVySFRNTCJdKTt3aW5kb3cuSFRNTFRlbXBsYXRlRWxlbWVudCYmRCh3aW5kb3cuSFRNTFRlbXBsYXRlRWxlbWVudC5wcm90b3R5cGUsWyJpbm5lckhUTUwiXSk7cGI/RChEb2N1bWVudEZyYWdtZW50LnByb3RvdHlwZSx3Yik6dGIoRG9jdW1lbnRGcmFnbWVudC5wcm90b3R5cGUsYSk7RChEb2N1bWVudEZyYWdtZW50LnByb3RvdHlwZSx4Yik7cGI/KEQoRG9jdW1lbnQucHJvdG90eXBlLHdiKSxEKERvY3VtZW50LnByb3RvdHlwZSxbImFjdGl2ZUVsZW1lbnQiXSkpOnRiKERvY3VtZW50LnByb3RvdHlwZSxhKTtEKERvY3VtZW50LnByb3RvdHlwZSxbImltcG9ydE5vZGUiLCJnZXRFbGVtZW50QnlJZCJdKTtEKERvY3VtZW50LnByb3RvdHlwZSx4Yil9O3ZhciB6Yj1CKHtnZXQgY2hpbGROb2Rlcygpe3JldHVybiB0aGlzLl9fc2hhZHlfY2hpbGROb2Rlc30sZ2V0IGZpcnN0Q2hpbGQoKXtyZXR1cm4gdGhpcy5fX3NoYWR5X2ZpcnN0Q2hpbGR9LGdldCBsYXN0Q2hpbGQoKXtyZXR1cm4gdGhpcy5fX3NoYWR5X2xhc3RDaGlsZH0sZ2V0IHRleHRDb250ZW50KCl7cmV0dXJuIHRoaXMuX19zaGFkeV90ZXh0Q29udGVudH0sc2V0IHRleHRDb250ZW50KGEpe3RoaXMuX19zaGFkeV90ZXh0Q29udGVudD1hfSxnZXQgY2hpbGRFbGVtZW50Q291bnQoKXtyZXR1cm4gdGhpcy5fX3NoYWR5X2NoaWxkRWxlbWVudENvdW50fSxnZXQgY2hpbGRyZW4oKXtyZXR1cm4gdGhpcy5fX3NoYWR5X2NoaWxkcmVufSxnZXQgZmlyc3RFbGVtZW50Q2hpbGQoKXtyZXR1cm4gdGhpcy5fX3NoYWR5X2ZpcnN0RWxlbWVudENoaWxkfSxnZXQgbGFzdEVsZW1lbnRDaGlsZCgpe3JldHVybiB0aGlzLl9fc2hhZHlfbGFzdEVsZW1lbnRDaGlsZH0sZ2V0IGlubmVySFRNTCgpe3JldHVybiB0aGlzLl9fc2hhZHlfaW5uZXJIVE1MfSwKc2V0IGlubmVySFRNTChhKXtyZXR1cm4gdGhpcy5fX3NoYWR5X2lubmVySFRNTD1hfSxnZXQgc2hhZG93Um9vdCgpe3JldHVybiB0aGlzLl9fc2hhZHlfc2hhZG93Um9vdH19KSxBYj1CKHtnZXQgcGFyZW50RWxlbWVudCgpe3JldHVybiB0aGlzLl9fc2hhZHlfcGFyZW50RWxlbWVudH0sZ2V0IHBhcmVudE5vZGUoKXtyZXR1cm4gdGhpcy5fX3NoYWR5X3BhcmVudE5vZGV9LGdldCBuZXh0U2libGluZygpe3JldHVybiB0aGlzLl9fc2hhZHlfbmV4dFNpYmxpbmd9LGdldCBwcmV2aW91c1NpYmxpbmcoKXtyZXR1cm4gdGhpcy5fX3NoYWR5X3ByZXZpb3VzU2libGluZ30sZ2V0IG5leHRFbGVtZW50U2libGluZygpe3JldHVybiB0aGlzLl9fc2hhZHlfbmV4dEVsZW1lbnRTaWJsaW5nfSxnZXQgcHJldmlvdXNFbGVtZW50U2libGluZygpe3JldHVybiB0aGlzLl9fc2hhZHlfcHJldmlvdXNFbGVtZW50U2libGluZ30sZ2V0IGNsYXNzTmFtZSgpe3JldHVybiB0aGlzLl9fc2hhZHlfY2xhc3NOYW1lfSwKc2V0IGNsYXNzTmFtZShhKXtyZXR1cm4gdGhpcy5fX3NoYWR5X2NsYXNzTmFtZT1hfX0pLEJiO2ZvcihCYiBpbiB6Yil6YltCYl0uZW51bWVyYWJsZT0hMTtmb3IodmFyIENiIGluIEFiKUFiW0NiXS5lbnVtZXJhYmxlPSExO3ZhciBEYj13Lm18fHcuSixFYj1EYj9mdW5jdGlvbigpe306ZnVuY3Rpb24oYSl7dmFyIGI9dShhKTtiLmxhfHwoYi5sYT0hMCxBKGEsQWIpKX0sRmI9RGI/ZnVuY3Rpb24oKXt9OmZ1bmN0aW9uKGEpe3ZhciBiPXUoYSk7Yi5rYXx8KGIua2E9ITAsQShhLHpiKSl9O3ZhciBHYj0iX19ldmVudFdyYXBwZXJzIitEYXRlLm5vdygpLEhiPWZ1bmN0aW9uKCl7dmFyIGE9T2JqZWN0LmdldE93blByb3BlcnR5RGVzY3JpcHRvcihFdmVudC5wcm90b3R5cGUsImNvbXBvc2VkIik7cmV0dXJuIGE/ZnVuY3Rpb24oYil7cmV0dXJuIGEuZ2V0LmNhbGwoYil9Om51bGx9KCksSWI9e2JsdXI6ITAsZm9jdXM6ITAsZm9jdXNpbjohMCxmb2N1c291dDohMCxjbGljazohMCxkYmxjbGljazohMCxtb3VzZWRvd246ITAsbW91c2VlbnRlcjohMCxtb3VzZWxlYXZlOiEwLG1vdXNlbW92ZTohMCxtb3VzZW91dDohMCxtb3VzZW92ZXI6ITAsbW91c2V1cDohMCx3aGVlbDohMCxiZWZvcmVpbnB1dDohMCxpbnB1dDohMCxrZXlkb3duOiEwLGtleXVwOiEwLGNvbXBvc2l0aW9uc3RhcnQ6ITAsY29tcG9zaXRpb251cGRhdGU6ITAsY29tcG9zaXRpb25lbmQ6ITAsdG91Y2hzdGFydDohMCx0b3VjaGVuZDohMCx0b3VjaG1vdmU6ITAsdG91Y2hjYW5jZWw6ITAscG9pbnRlcm92ZXI6ITAsCnBvaW50ZXJlbnRlcjohMCxwb2ludGVyZG93bjohMCxwb2ludGVybW92ZTohMCxwb2ludGVydXA6ITAscG9pbnRlcmNhbmNlbDohMCxwb2ludGVyb3V0OiEwLHBvaW50ZXJsZWF2ZTohMCxnb3Rwb2ludGVyY2FwdHVyZTohMCxsb3N0cG9pbnRlcmNhcHR1cmU6ITAsZHJhZ3N0YXJ0OiEwLGRyYWc6ITAsZHJhZ2VudGVyOiEwLGRyYWdsZWF2ZTohMCxkcmFnb3ZlcjohMCxkcm9wOiEwLGRyYWdlbmQ6ITAsRE9NQWN0aXZhdGU6ITAsRE9NRm9jdXNJbjohMCxET01Gb2N1c091dDohMCxrZXlwcmVzczohMH0sSmI9e0RPTUF0dHJNb2RpZmllZDohMCxET01BdHRyaWJ1dGVOYW1lQ2hhbmdlZDohMCxET01DaGFyYWN0ZXJEYXRhTW9kaWZpZWQ6ITAsRE9NRWxlbWVudE5hbWVDaGFuZ2VkOiEwLERPTU5vZGVJbnNlcnRlZDohMCxET01Ob2RlSW5zZXJ0ZWRJbnRvRG9jdW1lbnQ6ITAsRE9NTm9kZVJlbW92ZWQ6ITAsRE9NTm9kZVJlbW92ZWRGcm9tRG9jdW1lbnQ6ITAsRE9NU3VidHJlZU1vZGlmaWVkOiEwfTsKZnVuY3Rpb24gS2IoYSl7cmV0dXJuIGEgaW5zdGFuY2VvZiBOb2RlP2EuX19zaGFkeV9nZXRSb290Tm9kZSgpOmF9ZnVuY3Rpb24gTGIoYSxiKXt2YXIgYz1bXSxkPWE7Zm9yKGE9S2IoYSk7ZDspYy5wdXNoKGQpLGQuX19zaGFkeV9hc3NpZ25lZFNsb3Q/ZD1kLl9fc2hhZHlfYXNzaWduZWRTbG90OmQubm9kZVR5cGU9PT1Ob2RlLkRPQ1VNRU5UX0ZSQUdNRU5UX05PREUmJmQuaG9zdCYmKGJ8fGQhPT1hKT9kPWQuaG9zdDpkPWQuX19zaGFkeV9wYXJlbnROb2RlO2NbYy5sZW5ndGgtMV09PT1kb2N1bWVudCYmYy5wdXNoKHdpbmRvdyk7cmV0dXJuIGN9ZnVuY3Rpb24gTWIoYSl7YS5fX2NvbXBvc2VkUGF0aHx8KGEuX19jb21wb3NlZFBhdGg9TGIoYS50YXJnZXQsITApKTtyZXR1cm4gYS5fX2NvbXBvc2VkUGF0aH0KZnVuY3Rpb24gTmIoYSxiKXtpZigheilyZXR1cm4gYTthPUxiKGEsITApO2Zvcih2YXIgYz0wLGQsZT12b2lkIDAsZixnPXZvaWQgMDtjPGIubGVuZ3RoO2MrKylpZihkPWJbY10sZj1LYihkKSxmIT09ZSYmKGc9YS5pbmRleE9mKGYpLGU9ZiksIXooZil8fC0xPGcpcmV0dXJuIGR9ZnVuY3Rpb24gT2IoYSl7ZnVuY3Rpb24gYihiLGQpe2I9bmV3IGEoYixkKTtiLl9fY29tcG9zZWQ9ZCYmISFkLmNvbXBvc2VkO3JldHVybiBifWIuX19wcm90b19fPWE7Yi5wcm90b3R5cGU9YS5wcm90b3R5cGU7cmV0dXJuIGJ9dmFyIFBiPXtmb2N1czohMCxibHVyOiEwfTtmdW5jdGlvbiBRYihhKXtyZXR1cm4gYS5fX3RhcmdldCE9PWEudGFyZ2V0fHxhLl9fcmVsYXRlZFRhcmdldCE9PWEucmVsYXRlZFRhcmdldH0KZnVuY3Rpb24gUmIoYSxiLGMpe2lmKGM9Yi5fX2hhbmRsZXJzJiZiLl9faGFuZGxlcnNbYS50eXBlXSYmYi5fX2hhbmRsZXJzW2EudHlwZV1bY10pZm9yKHZhciBkPTAsZTsoZT1jW2RdKSYmKCFRYihhKXx8YS50YXJnZXQhPT1hLnJlbGF0ZWRUYXJnZXQpJiYoZS5jYWxsKGIsYSksIWEuX19pbW1lZGlhdGVQcm9wYWdhdGlvblN0b3BwZWQpO2QrKyk7fQpmdW5jdGlvbiBTYihhKXt2YXIgYj1hLmNvbXBvc2VkUGF0aCgpO09iamVjdC5kZWZpbmVQcm9wZXJ0eShhLCJjdXJyZW50VGFyZ2V0Iix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIGR9LGNvbmZpZ3VyYWJsZTohMH0pO2Zvcih2YXIgYz1iLmxlbmd0aC0xOzA8PWM7Yy0tKXt2YXIgZD1iW2NdO1JiKGEsZCwiY2FwdHVyZSIpO2lmKGEuWilyZXR1cm59T2JqZWN0LmRlZmluZVByb3BlcnR5KGEsImV2ZW50UGhhc2UiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gRXZlbnQuQVRfVEFSR0VUfX0pO3ZhciBlO2ZvcihjPTA7YzxiLmxlbmd0aDtjKyspe2Q9YltjXTt2YXIgZj12KGQpO2Y9ZiYmZi5yb290O2lmKDA9PT1jfHxmJiZmPT09ZSlpZihSYihhLGQsImJ1YmJsZSIpLGQhPT13aW5kb3cmJihlPWQuX19zaGFkeV9nZXRSb290Tm9kZSgpKSxhLlopYnJlYWt9fQpmdW5jdGlvbiBUYihhLGIsYyxkLGUsZil7Zm9yKHZhciBnPTA7ZzxhLmxlbmd0aDtnKyspe3ZhciBoPWFbZ10saz1oLnR5cGUsbD1oLmNhcHR1cmUsbT1oLm9uY2UscT1oLnBhc3NpdmU7aWYoYj09PWgubm9kZSYmYz09PWsmJmQ9PT1sJiZlPT09bSYmZj09PXEpcmV0dXJuIGd9cmV0dXJuLTF9CmZ1bmN0aW9uIFViKGEsYixjKXtpZihiKXt2YXIgZD10eXBlb2YgYjtpZigiZnVuY3Rpb24iPT09ZHx8Im9iamVjdCI9PT1kKWlmKCJvYmplY3QiIT09ZHx8Yi5oYW5kbGVFdmVudCYmImZ1bmN0aW9uIj09PXR5cGVvZiBiLmhhbmRsZUV2ZW50KXtpZihKYlthXSlyZXR1cm4gdGhpcy5fX3NoYWR5X25hdGl2ZV9hZGRFdmVudExpc3RlbmVyKGEsYixjKTtpZihjJiYib2JqZWN0Ij09PXR5cGVvZiBjKXt2YXIgZT0hIWMuY2FwdHVyZTt2YXIgZj0hIWMub25jZTt2YXIgZz0hIWMucGFzc2l2ZX1lbHNlIGU9ISFjLGc9Zj0hMTt2YXIgaD1jJiZjLiR8fHRoaXMsaz1iW0diXTtpZihrKXtpZigtMTxUYihrLGgsYSxlLGYsZykpcmV0dXJufWVsc2UgYltHYl09W107az1mdW5jdGlvbihlKXtmJiZ0aGlzLl9fc2hhZHlfcmVtb3ZlRXZlbnRMaXN0ZW5lcihhLGIsYyk7ZS5fX3RhcmdldHx8VmIoZSk7aWYoaCE9PXRoaXMpe3ZhciBnPU9iamVjdC5nZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3IoZSwiY3VycmVudFRhcmdldCIpOwpPYmplY3QuZGVmaW5lUHJvcGVydHkoZSwiY3VycmVudFRhcmdldCIse2dldDpmdW5jdGlvbigpe3JldHVybiBofSxjb25maWd1cmFibGU6ITB9KX1lLl9fcHJldmlvdXNDdXJyZW50VGFyZ2V0PWUuY3VycmVudFRhcmdldDtpZigheihoKXx8LTEhPWUuY29tcG9zZWRQYXRoKCkuaW5kZXhPZihoKSlpZihlLmNvbXBvc2VkfHwtMTxlLmNvbXBvc2VkUGF0aCgpLmluZGV4T2YoaCkpaWYoUWIoZSkmJmUudGFyZ2V0PT09ZS5yZWxhdGVkVGFyZ2V0KWUuZXZlbnRQaGFzZT09PUV2ZW50LkJVQkJMSU5HX1BIQVNFJiZlLnN0b3BJbW1lZGlhdGVQcm9wYWdhdGlvbigpO2Vsc2UgaWYoZS5ldmVudFBoYXNlPT09RXZlbnQuQ0FQVFVSSU5HX1BIQVNFfHxlLmJ1YmJsZXN8fGUudGFyZ2V0PT09aHx8aCBpbnN0YW5jZW9mIFdpbmRvdyl7dmFyIGs9ImZ1bmN0aW9uIj09PWQ/Yi5jYWxsKGgsZSk6Yi5oYW5kbGVFdmVudCYmYi5oYW5kbGVFdmVudChlKTtoIT09dGhpcyYmKGc/KE9iamVjdC5kZWZpbmVQcm9wZXJ0eShlLAoiY3VycmVudFRhcmdldCIsZyksZz1udWxsKTpkZWxldGUgZS5jdXJyZW50VGFyZ2V0KTtyZXR1cm4ga319O2JbR2JdLnB1c2goe25vZGU6aCx0eXBlOmEsY2FwdHVyZTplLG9uY2U6ZixwYXNzaXZlOmcsWWE6a30pO1BiW2FdPyh0aGlzLl9faGFuZGxlcnM9dGhpcy5fX2hhbmRsZXJzfHx7fSx0aGlzLl9faGFuZGxlcnNbYV09dGhpcy5fX2hhbmRsZXJzW2FdfHx7Y2FwdHVyZTpbXSxidWJibGU6W119LHRoaXMuX19oYW5kbGVyc1thXVtlPyJjYXB0dXJlIjoiYnViYmxlIl0ucHVzaChrKSk6dGhpcy5fX3NoYWR5X25hdGl2ZV9hZGRFdmVudExpc3RlbmVyKGEsayxjKX19fQpmdW5jdGlvbiBXYihhLGIsYyl7aWYoYil7aWYoSmJbYV0pcmV0dXJuIHRoaXMuX19zaGFkeV9uYXRpdmVfcmVtb3ZlRXZlbnRMaXN0ZW5lcihhLGIsYyk7aWYoYyYmIm9iamVjdCI9PT10eXBlb2YgYyl7dmFyIGQ9ISFjLmNhcHR1cmU7dmFyIGU9ISFjLm9uY2U7dmFyIGY9ISFjLnBhc3NpdmV9ZWxzZSBkPSEhYyxmPWU9ITE7dmFyIGc9YyYmYy4kfHx0aGlzLGg9dm9pZCAwO3ZhciBrPW51bGw7dHJ5e2s9YltHYl19Y2F0Y2gobCl7fWsmJihlPVRiKGssZyxhLGQsZSxmKSwtMTxlJiYoaD1rLnNwbGljZShlLDEpWzBdLllhLGsubGVuZ3RofHwoYltHYl09dm9pZCAwKSkpO3RoaXMuX19zaGFkeV9uYXRpdmVfcmVtb3ZlRXZlbnRMaXN0ZW5lcihhLGh8fGIsYyk7aCYmUGJbYV0mJnRoaXMuX19oYW5kbGVycyYmdGhpcy5fX2hhbmRsZXJzW2FdJiYoYT10aGlzLl9faGFuZGxlcnNbYV1bZD8iY2FwdHVyZSI6ImJ1YmJsZSJdLGg9YS5pbmRleE9mKGgpLC0xPGgmJmEuc3BsaWNlKGgsMSkpfX0KZnVuY3Rpb24gWGIoKXtmb3IodmFyIGEgaW4gUGIpd2luZG93Ll9fc2hhZHlfbmF0aXZlX2FkZEV2ZW50TGlzdGVuZXIoYSxmdW5jdGlvbihhKXthLl9fdGFyZ2V0fHwoVmIoYSksU2IoYSkpfSwhMCl9CnZhciBZYj1CKHtnZXQgY29tcG9zZWQoKXt2b2lkIDA9PT10aGlzLl9fY29tcG9zZWQmJihIYj90aGlzLl9fY29tcG9zZWQ9ImZvY3VzaW4iPT09dGhpcy50eXBlfHwiZm9jdXNvdXQiPT09dGhpcy50eXBlfHxIYih0aGlzKTohMSE9PXRoaXMuaXNUcnVzdGVkJiYodGhpcy5fX2NvbXBvc2VkPUliW3RoaXMudHlwZV0pKTtyZXR1cm4gdGhpcy5fX2NvbXBvc2VkfHwhMX0sY29tcG9zZWRQYXRoOmZ1bmN0aW9uKCl7dGhpcy5fX2NvbXBvc2VkUGF0aHx8KHRoaXMuX19jb21wb3NlZFBhdGg9TGIodGhpcy5fX3RhcmdldCx0aGlzLmNvbXBvc2VkKSk7cmV0dXJuIHRoaXMuX19jb21wb3NlZFBhdGh9LGdldCB0YXJnZXQoKXtyZXR1cm4gTmIodGhpcy5jdXJyZW50VGFyZ2V0fHx0aGlzLl9fcHJldmlvdXNDdXJyZW50VGFyZ2V0LHRoaXMuY29tcG9zZWRQYXRoKCkpfSxnZXQgcmVsYXRlZFRhcmdldCgpe2lmKCF0aGlzLl9fcmVsYXRlZFRhcmdldClyZXR1cm4gbnVsbDt0aGlzLl9fcmVsYXRlZFRhcmdldENvbXBvc2VkUGF0aHx8Cih0aGlzLl9fcmVsYXRlZFRhcmdldENvbXBvc2VkUGF0aD1MYih0aGlzLl9fcmVsYXRlZFRhcmdldCwhMCkpO3JldHVybiBOYih0aGlzLmN1cnJlbnRUYXJnZXR8fHRoaXMuX19wcmV2aW91c0N1cnJlbnRUYXJnZXQsdGhpcy5fX3JlbGF0ZWRUYXJnZXRDb21wb3NlZFBhdGgpfSxzdG9wUHJvcGFnYXRpb246ZnVuY3Rpb24oKXtFdmVudC5wcm90b3R5cGUuc3RvcFByb3BhZ2F0aW9uLmNhbGwodGhpcyk7dGhpcy5aPSEwfSxzdG9wSW1tZWRpYXRlUHJvcGFnYXRpb246ZnVuY3Rpb24oKXtFdmVudC5wcm90b3R5cGUuc3RvcEltbWVkaWF0ZVByb3BhZ2F0aW9uLmNhbGwodGhpcyk7dGhpcy5aPXRoaXMuX19pbW1lZGlhdGVQcm9wYWdhdGlvblN0b3BwZWQ9ITB9fSk7CmZ1bmN0aW9uIFZiKGEpe2EuX190YXJnZXQ9YS50YXJnZXQ7YS5fX3JlbGF0ZWRUYXJnZXQ9YS5yZWxhdGVkVGFyZ2V0O2lmKHcubSl7dmFyIGI9T2JqZWN0LmdldFByb3RvdHlwZU9mKGEpO2lmKCFPYmplY3QuaGFzT3duUHJvcGVydHkoYiwiX19zaGFkeV9wYXRjaGVkUHJvdG8iKSl7dmFyIGM9T2JqZWN0LmNyZWF0ZShiKTtjLl9fc2hhZHlfc291cmNlUHJvdG89YjtBKGMsWWIpO2IuX19zaGFkeV9wYXRjaGVkUHJvdG89Y31hLl9fcHJvdG9fXz1iLl9fc2hhZHlfcGF0Y2hlZFByb3RvfWVsc2UgQShhLFliKX12YXIgWmI9T2IoRXZlbnQpLCRiPU9iKEN1c3RvbUV2ZW50KSxhYz1PYihNb3VzZUV2ZW50KTsKZnVuY3Rpb24gYmMoKXtpZighSGImJk9iamVjdC5nZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3IoRXZlbnQucHJvdG90eXBlLCJpc1RydXN0ZWQiKSl7dmFyIGE9ZnVuY3Rpb24oKXt2YXIgYT1uZXcgTW91c2VFdmVudCgiY2xpY2siLHtidWJibGVzOiEwLGNhbmNlbGFibGU6ITAsY29tcG9zZWQ6ITB9KTt0aGlzLl9fc2hhZHlfZGlzcGF0Y2hFdmVudChhKX07RWxlbWVudC5wcm90b3R5cGUuY2xpY2s/RWxlbWVudC5wcm90b3R5cGUuY2xpY2s9YTpIVE1MRWxlbWVudC5wcm90b3R5cGUuY2xpY2smJihIVE1MRWxlbWVudC5wcm90b3R5cGUuY2xpY2s9YSl9fXZhciBjYz1PYmplY3QuZ2V0T3duUHJvcGVydHlOYW1lcyhEb2N1bWVudC5wcm90b3R5cGUpLmZpbHRlcihmdW5jdGlvbihhKXtyZXR1cm4ib24iPT09YS5zdWJzdHJpbmcoMCwyKX0pO2Z1bmN0aW9uIGRjKGEsYil7cmV0dXJue2luZGV4OmEsTDpbXSxSOmJ9fQpmdW5jdGlvbiBlYyhhLGIsYyxkKXt2YXIgZT0wLGY9MCxnPTAsaD0wLGs9TWF0aC5taW4oYi1lLGQtZik7aWYoMD09ZSYmMD09ZilhOntmb3IoZz0wO2c8aztnKyspaWYoYVtnXSE9PWNbZ10pYnJlYWsgYTtnPWt9aWYoYj09YS5sZW5ndGgmJmQ9PWMubGVuZ3RoKXtoPWEubGVuZ3RoO2Zvcih2YXIgbD1jLmxlbmd0aCxtPTA7bTxrLWcmJmZjKGFbLS1oXSxjWy0tbF0pOyltKys7aD1tfWUrPWc7Zis9ZztiLT1oO2QtPWg7aWYoMD09Yi1lJiYwPT1kLWYpcmV0dXJuW107aWYoZT09Yil7Zm9yKGI9ZGMoZSwwKTtmPGQ7KWIuTC5wdXNoKGNbZisrXSk7cmV0dXJuW2JdfWlmKGY9PWQpcmV0dXJuW2RjKGUsYi1lKV07az1lO2c9ZjtkPWQtZysxO2g9Yi1rKzE7Yj1BcnJheShkKTtmb3IobD0wO2w8ZDtsKyspYltsXT1BcnJheShoKSxiW2xdWzBdPWw7Zm9yKGw9MDtsPGg7bCsrKWJbMF1bbF09bDtmb3IobD0xO2w8ZDtsKyspZm9yKG09MTttPGg7bSsrKWlmKGFbayttLTFdPT09Y1tnK2wtMV0pYltsXVttXT0KYltsLTFdW20tMV07ZWxzZXt2YXIgcT1iW2wtMV1bbV0rMSx4PWJbbF1bbS0xXSsxO2JbbF1bbV09cTx4P3E6eH1rPWIubGVuZ3RoLTE7Zz1iWzBdLmxlbmd0aC0xO2Q9YltrXVtnXTtmb3IoYT1bXTswPGt8fDA8ZzspMD09az8oYS5wdXNoKDIpLGctLSk6MD09Zz8oYS5wdXNoKDMpLGstLSk6KGg9YltrLTFdW2ctMV0sbD1iW2stMV1bZ10sbT1iW2tdW2ctMV0scT1sPG0/bDxoP2w6aDptPGg/bTpoLHE9PWg/KGg9PWQ/YS5wdXNoKDApOihhLnB1c2goMSksZD1oKSxrLS0sZy0tKTpxPT1sPyhhLnB1c2goMyksay0tLGQ9bCk6KGEucHVzaCgyKSxnLS0sZD1tKSk7YS5yZXZlcnNlKCk7Yj12b2lkIDA7az1bXTtmb3IoZz0wO2c8YS5sZW5ndGg7ZysrKXN3aXRjaChhW2ddKXtjYXNlIDA6YiYmKGsucHVzaChiKSxiPXZvaWQgMCk7ZSsrO2YrKzticmVhaztjYXNlIDE6Ynx8KGI9ZGMoZSwwKSk7Yi5SKys7ZSsrO2IuTC5wdXNoKGNbZl0pO2YrKzticmVhaztjYXNlIDI6Ynx8KGI9ZGMoZSwwKSk7CmIuUisrO2UrKzticmVhaztjYXNlIDM6Ynx8KGI9ZGMoZSwwKSksYi5MLnB1c2goY1tmXSksZisrfWImJmsucHVzaChiKTtyZXR1cm4ga31mdW5jdGlvbiBmYyhhLGIpe3JldHVybiBhPT09Yn07ZnVuY3Rpb24gZ2MoYSxiLGMpe0ViKGEpO2M9Y3x8bnVsbDt2YXIgZD11KGEpLGU9dShiKSxmPWM/dShjKTpudWxsO2QucHJldmlvdXNTaWJsaW5nPWM/Zi5wcmV2aW91c1NpYmxpbmc6Yi5fX3NoYWR5X2xhc3RDaGlsZDtpZihmPXYoZC5wcmV2aW91c1NpYmxpbmcpKWYubmV4dFNpYmxpbmc9YTtpZihmPXYoZC5uZXh0U2libGluZz1jKSlmLnByZXZpb3VzU2libGluZz1hO2QucGFyZW50Tm9kZT1iO2M/Yz09PWUuZmlyc3RDaGlsZCYmKGUuZmlyc3RDaGlsZD1hKTooZS5sYXN0Q2hpbGQ9YSxlLmZpcnN0Q2hpbGR8fChlLmZpcnN0Q2hpbGQ9YSkpO2UuY2hpbGROb2Rlcz1udWxsfQpmdW5jdGlvbiBoYyhhLGIsYyl7RmIoYik7dmFyIGQ9dShiKTt2b2lkIDAhPT1kLmZpcnN0Q2hpbGQmJihkLmNoaWxkTm9kZXM9bnVsbCk7aWYoYS5ub2RlVHlwZT09PU5vZGUuRE9DVU1FTlRfRlJBR01FTlRfTk9ERSl7ZD1hLl9fc2hhZHlfY2hpbGROb2Rlcztmb3IodmFyIGU9MDtlPGQubGVuZ3RoO2UrKylnYyhkW2VdLGIsYyk7YT11KGEpO2I9dm9pZCAwIT09YS5maXJzdENoaWxkP251bGw6dm9pZCAwO2EuZmlyc3RDaGlsZD1hLmxhc3RDaGlsZD1iO2EuY2hpbGROb2Rlcz1ifWVsc2UgZ2MoYSxiLGMpfQpmdW5jdGlvbiBpYyhhLGIpe3ZhciBjPXUoYSk7Yj11KGIpO2E9PT1iLmZpcnN0Q2hpbGQmJihiLmZpcnN0Q2hpbGQ9Yy5uZXh0U2libGluZyk7YT09PWIubGFzdENoaWxkJiYoYi5sYXN0Q2hpbGQ9Yy5wcmV2aW91c1NpYmxpbmcpO2E9Yy5wcmV2aW91c1NpYmxpbmc7dmFyIGQ9Yy5uZXh0U2libGluZzthJiYodShhKS5uZXh0U2libGluZz1kKTtkJiYodShkKS5wcmV2aW91c1NpYmxpbmc9YSk7Yy5wYXJlbnROb2RlPWMucHJldmlvdXNTaWJsaW5nPWMubmV4dFNpYmxpbmc9dm9pZCAwO3ZvaWQgMCE9PWIuY2hpbGROb2RlcyYmKGIuY2hpbGROb2Rlcz1udWxsKX0KZnVuY3Rpb24gamMoYSl7dmFyIGI9dShhKTtpZih2b2lkIDA9PT1iLmZpcnN0Q2hpbGQpe2IuY2hpbGROb2Rlcz1udWxsO3ZhciBjPWIuZmlyc3RDaGlsZD1hLl9fc2hhZHlfbmF0aXZlX2ZpcnN0Q2hpbGR8fG51bGw7Yi5sYXN0Q2hpbGQ9YS5fX3NoYWR5X25hdGl2ZV9sYXN0Q2hpbGR8fG51bGw7RmIoYSk7Yj1jO2ZvcihjPXZvaWQgMDtiO2I9Yi5fX3NoYWR5X25hdGl2ZV9uZXh0U2libGluZyl7dmFyIGQ9dShiKTtkLnBhcmVudE5vZGU9YTtkLm5leHRTaWJsaW5nPWIuX19zaGFkeV9uYXRpdmVfbmV4dFNpYmxpbmd8fG51bGw7ZC5wcmV2aW91c1NpYmxpbmc9Y3x8bnVsbDtjPWI7RWIoYil9fX07dmFyIGtjPW51bGw7ZnVuY3Rpb24gRygpe2tjfHwoa2M9d2luZG93LlNoYWR5Q1NTJiZ3aW5kb3cuU2hhZHlDU1MuU2NvcGluZ1NoaW0pO3JldHVybiBrY3x8bnVsbH1mdW5jdGlvbiBsYyhhLGIpe3ZhciBjPUcoKTtjJiZjLnVuc2NvcGVOb2RlKGEsYil9ZnVuY3Rpb24gbWMoYSxiKXt2YXIgYz1HKCk7aWYoIWMpcmV0dXJuITA7aWYoYS5ub2RlVHlwZT09PU5vZGUuRE9DVU1FTlRfRlJBR01FTlRfTk9ERSl7Yz0hMDthPWEuX19zaGFkeV9jaGlsZE5vZGVzO2Zvcih2YXIgZD0wO2MmJmQ8YS5sZW5ndGg7ZCsrKWM9YyYmbWMoYVtkXSxiKTtyZXR1cm4gY31yZXR1cm4gYS5ub2RlVHlwZSE9PU5vZGUuRUxFTUVOVF9OT0RFPyEwOmMuY3VycmVudFNjb3BlRm9yTm9kZShhKT09PWJ9ZnVuY3Rpb24gbmMoYSl7aWYoYS5ub2RlVHlwZSE9PU5vZGUuRUxFTUVOVF9OT0RFKXJldHVybiIiO3ZhciBiPUcoKTtyZXR1cm4gYj9iLmN1cnJlbnRTY29wZUZvck5vZGUoYSk6IiJ9CmZ1bmN0aW9uIG9jKGEsYil7aWYoYSl7YS5ub2RlVHlwZT09PU5vZGUuRUxFTUVOVF9OT0RFJiZiKGEpO2E9YS5fX3NoYWR5X2NoaWxkTm9kZXM7Zm9yKHZhciBjPTAsZDtjPGEubGVuZ3RoO2MrKylkPWFbY10sZC5ub2RlVHlwZT09PU5vZGUuRUxFTUVOVF9OT0RFJiZvYyhkLGIpfX07dmFyIHBjPXdpbmRvdy5kb2N1bWVudCxxYz13Lm9hLHJjPU9iamVjdC5nZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3IoTm9kZS5wcm90b3R5cGUsImlzQ29ubmVjdGVkIiksc2M9cmMmJnJjLmdldDtmdW5jdGlvbiB0YyhhKXtmb3IodmFyIGI7Yj1hLl9fc2hhZHlfZmlyc3RDaGlsZDspYS5fX3NoYWR5X3JlbW92ZUNoaWxkKGIpfWZ1bmN0aW9uIHVjKGEpe3ZhciBiPXYoYSk7aWYoYiYmdm9pZCAwIT09Yi5LKXtiPWEuX19zaGFkeV9jaGlsZE5vZGVzO2Zvcih2YXIgYz0wLGQ9Yi5sZW5ndGgsZT12b2lkIDA7YzxkJiYoZT1iW2NdKTtjKyspdWMoZSl9aWYoYT12KGEpKWEuSz12b2lkIDB9ZnVuY3Rpb24gdmMoYSl7dmFyIGI9YTthJiYic2xvdCI9PT1hLmxvY2FsTmFtZSYmKGI9KGI9KGI9dihhKSkmJmIuRCkmJmIubGVuZ3RoP2JbMF06dmMoYS5fX3NoYWR5X25leHRTaWJsaW5nKSk7cmV0dXJuIGJ9CmZ1bmN0aW9uIHdjKGEsYixjKXtpZihhPShhPXYoYSkpJiZhLkgpYiYmYS5hZGRlZE5vZGVzLnB1c2goYiksYyYmYS5yZW1vdmVkTm9kZXMucHVzaChjKSxhYihhKX0KdmFyIENjPUIoe2dldCBwYXJlbnROb2RlKCl7dmFyIGE9dih0aGlzKTthPWEmJmEucGFyZW50Tm9kZTtyZXR1cm4gdm9pZCAwIT09YT9hOnRoaXMuX19zaGFkeV9uYXRpdmVfcGFyZW50Tm9kZX0sZ2V0IGZpcnN0Q2hpbGQoKXt2YXIgYT12KHRoaXMpO2E9YSYmYS5maXJzdENoaWxkO3JldHVybiB2b2lkIDAhPT1hP2E6dGhpcy5fX3NoYWR5X25hdGl2ZV9maXJzdENoaWxkfSxnZXQgbGFzdENoaWxkKCl7dmFyIGE9dih0aGlzKTthPWEmJmEubGFzdENoaWxkO3JldHVybiB2b2lkIDAhPT1hP2E6dGhpcy5fX3NoYWR5X25hdGl2ZV9sYXN0Q2hpbGR9LGdldCBuZXh0U2libGluZygpe3ZhciBhPXYodGhpcyk7YT1hJiZhLm5leHRTaWJsaW5nO3JldHVybiB2b2lkIDAhPT1hP2E6dGhpcy5fX3NoYWR5X25hdGl2ZV9uZXh0U2libGluZ30sZ2V0IHByZXZpb3VzU2libGluZygpe3ZhciBhPXYodGhpcyk7YT1hJiZhLnByZXZpb3VzU2libGluZztyZXR1cm4gdm9pZCAwIT09YT9hOnRoaXMuX19zaGFkeV9uYXRpdmVfcHJldmlvdXNTaWJsaW5nfSwKZ2V0IGNoaWxkTm9kZXMoKXtpZih5KHRoaXMpKXt2YXIgYT12KHRoaXMpO2lmKCFhLmNoaWxkTm9kZXMpe2EuY2hpbGROb2Rlcz1bXTtmb3IodmFyIGI9dGhpcy5fX3NoYWR5X2ZpcnN0Q2hpbGQ7YjtiPWIuX19zaGFkeV9uZXh0U2libGluZylhLmNoaWxkTm9kZXMucHVzaChiKX12YXIgYz1hLmNoaWxkTm9kZXN9ZWxzZSBjPXRoaXMuX19zaGFkeV9uYXRpdmVfY2hpbGROb2RlcztjLml0ZW09ZnVuY3Rpb24oYSl7cmV0dXJuIGNbYV19O3JldHVybiBjfSxnZXQgcGFyZW50RWxlbWVudCgpe3ZhciBhPXYodGhpcyk7KGE9YSYmYS5wYXJlbnROb2RlKSYmYS5ub2RlVHlwZSE9PU5vZGUuRUxFTUVOVF9OT0RFJiYoYT1udWxsKTtyZXR1cm4gdm9pZCAwIT09YT9hOnRoaXMuX19zaGFkeV9uYXRpdmVfcGFyZW50RWxlbWVudH0sZ2V0IGlzQ29ubmVjdGVkKCl7aWYoc2MmJnNjLmNhbGwodGhpcykpcmV0dXJuITA7aWYodGhpcy5ub2RlVHlwZT09Tm9kZS5ET0NVTUVOVF9GUkFHTUVOVF9OT0RFKXJldHVybiExOwp2YXIgYT10aGlzLm93bmVyRG9jdW1lbnQ7aWYoVGEpe2lmKGEuX19zaGFkeV9uYXRpdmVfY29udGFpbnModGhpcykpcmV0dXJuITB9ZWxzZSBpZihhLmRvY3VtZW50RWxlbWVudCYmYS5kb2N1bWVudEVsZW1lbnQuX19zaGFkeV9uYXRpdmVfY29udGFpbnModGhpcykpcmV0dXJuITA7Zm9yKGE9dGhpczthJiYhKGEgaW5zdGFuY2VvZiBEb2N1bWVudCk7KWE9YS5fX3NoYWR5X3BhcmVudE5vZGV8fCh6KGEpP2EuaG9zdDp2b2lkIDApO3JldHVybiEhKGEmJmEgaW5zdGFuY2VvZiBEb2N1bWVudCl9LGdldCB0ZXh0Q29udGVudCgpe2lmKHkodGhpcykpe2Zvcih2YXIgYT1bXSxiPTAsYz10aGlzLl9fc2hhZHlfY2hpbGROb2RlcyxkO2Q9Y1tiXTtiKyspZC5ub2RlVHlwZSE9PU5vZGUuQ09NTUVOVF9OT0RFJiZhLnB1c2goZC5fX3NoYWR5X3RleHRDb250ZW50KTtyZXR1cm4gYS5qb2luKCIiKX1yZXR1cm4gdGhpcy5fX3NoYWR5X25hdGl2ZV90ZXh0Q29udGVudH0sc2V0IHRleHRDb250ZW50KGEpe2lmKCJ1bmRlZmluZWQiPT09CnR5cGVvZiBhfHxudWxsPT09YSlhPSIiO3N3aXRjaCh0aGlzLm5vZGVUeXBlKXtjYXNlIE5vZGUuRUxFTUVOVF9OT0RFOmNhc2UgTm9kZS5ET0NVTUVOVF9GUkFHTUVOVF9OT0RFOmlmKCF5KHRoaXMpJiZ3Lm0pe3ZhciBiPXRoaXMuX19zaGFkeV9maXJzdENoaWxkOyhiIT10aGlzLl9fc2hhZHlfbGFzdENoaWxkfHxiJiZiLm5vZGVUeXBlIT1Ob2RlLlRFWFRfTk9ERSkmJnRjKHRoaXMpO3RoaXMuX19zaGFkeV9uYXRpdmVfdGV4dENvbnRlbnQ9YX1lbHNlIHRjKHRoaXMpLCgwPGEubGVuZ3RofHx0aGlzLm5vZGVUeXBlPT09Tm9kZS5FTEVNRU5UX05PREUpJiZ0aGlzLl9fc2hhZHlfaW5zZXJ0QmVmb3JlKGRvY3VtZW50LmNyZWF0ZVRleHROb2RlKGEpKTticmVhaztkZWZhdWx0OnRoaXMubm9kZVZhbHVlPWF9fSxpbnNlcnRCZWZvcmU6ZnVuY3Rpb24oYSxiKXtpZih0aGlzLm93bmVyRG9jdW1lbnQhPT1wYyYmYS5vd25lckRvY3VtZW50IT09cGMpcmV0dXJuIHRoaXMuX19zaGFkeV9uYXRpdmVfaW5zZXJ0QmVmb3JlKGEsCmIpLGE7aWYoYT09PXRoaXMpdGhyb3cgRXJyb3IoIkZhaWxlZCB0byBleGVjdXRlICdhcHBlbmRDaGlsZCcgb24gJ05vZGUnOiBUaGUgbmV3IGNoaWxkIGVsZW1lbnQgY29udGFpbnMgdGhlIHBhcmVudC4iKTtpZihiKXt2YXIgYz12KGIpO2M9YyYmYy5wYXJlbnROb2RlO2lmKHZvaWQgMCE9PWMmJmMhPT10aGlzfHx2b2lkIDA9PT1jJiZiLl9fc2hhZHlfbmF0aXZlX3BhcmVudE5vZGUhPT10aGlzKXRocm93IEVycm9yKCJGYWlsZWQgdG8gZXhlY3V0ZSAnaW5zZXJ0QmVmb3JlJyBvbiAnTm9kZSc6IFRoZSBub2RlIGJlZm9yZSB3aGljaCB0aGUgbmV3IG5vZGUgaXMgdG8gYmUgaW5zZXJ0ZWQgaXMgbm90IGEgY2hpbGQgb2YgdGhpcyBub2RlLiIpO31pZihiPT09YSlyZXR1cm4gYTt2YXIgZD1bXSxlPShjPXhjKHRoaXMpKT9jLmhvc3QubG9jYWxOYW1lOm5jKHRoaXMpLGY9YS5fX3NoYWR5X3BhcmVudE5vZGU7aWYoZil7dmFyIGc9bmMoYSk7Zi5fX3NoYWR5X3JlbW92ZUNoaWxkKGEsISFjfHwKIXhjKGEpKX1mPSEwO3ZhciBoPSghcWN8fHZvaWQgMD09PWEuX19ub0luc2VydGlvblBvaW50KSYmIW1jKGEsZSksaz1jJiYhYS5fX25vSW5zZXJ0aW9uUG9pbnQmJighcWN8fGEubm9kZVR5cGU9PT1Ob2RlLkRPQ1VNRU5UX0ZSQUdNRU5UX05PREUpO2lmKGt8fGgpaCYmKGc9Z3x8bmMoYSkpLG9jKGEsZnVuY3Rpb24oYSl7ayYmInNsb3QiPT09YS5sb2NhbE5hbWUmJmQucHVzaChhKTtpZihoKXt2YXIgYj1nO0coKSYmKGImJmxjKGEsYiksKGI9RygpKSYmYi5zY29wZU5vZGUoYSxlKSl9fSk7aWYoInNsb3QiPT09dGhpcy5sb2NhbE5hbWV8fGQubGVuZ3RoKWQubGVuZ3RoJiYoYy5jPWMuY3x8W10sYy5hPWMuYXx8W10sYy5iPWMuYnx8e30sYy5jLnB1c2guYXBwbHkoYy5jLGQgaW5zdGFuY2VvZiBBcnJheT9kOmphKGlhKGQpKSkpLGMmJkFjKGMpO3kodGhpcykmJihoYyhhLHRoaXMsYiksYz12KHRoaXMpLExhKHRoaXMpPyhBYyhjLnJvb3QpLGY9ITEpOmMucm9vdCYmKGY9ITEpKTtmPwooYz16KHRoaXMpP3RoaXMuaG9zdDp0aGlzLGI/KGI9dmMoYiksYy5fX3NoYWR5X25hdGl2ZV9pbnNlcnRCZWZvcmUoYSxiKSk6Yy5fX3NoYWR5X25hdGl2ZV9hcHBlbmRDaGlsZChhKSk6YS5vd25lckRvY3VtZW50IT09dGhpcy5vd25lckRvY3VtZW50JiZ0aGlzLm93bmVyRG9jdW1lbnQuYWRvcHROb2RlKGEpO3djKHRoaXMsYSk7cmV0dXJuIGF9LGFwcGVuZENoaWxkOmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLl9fc2hhZHlfaW5zZXJ0QmVmb3JlKGEpfSxyZW1vdmVDaGlsZDpmdW5jdGlvbihhLGIpe2I9dm9pZCAwPT09Yj8hMTpiO2lmKHRoaXMub3duZXJEb2N1bWVudCE9PXBjKXJldHVybiB0aGlzLl9fc2hhZHlfbmF0aXZlX3JlbW92ZUNoaWxkKGEpO2lmKGEuX19zaGFkeV9wYXJlbnROb2RlIT09dGhpcyl0aHJvdyBFcnJvcigiVGhlIG5vZGUgdG8gYmUgcmVtb3ZlZCBpcyBub3QgYSBjaGlsZCBvZiB0aGlzIG5vZGU6ICIrYSk7dmFyIGM9eGMoYSksZD1jJiZCYyhjLGEpLGU9dih0aGlzKTsKaWYoeSh0aGlzKSYmKGljKGEsdGhpcyksTGEodGhpcykpKXtBYyhlLnJvb3QpO3ZhciBmPSEwfWlmKEcoKSYmIWImJmMpe3ZhciBnPW5jKGEpO29jKGEsZnVuY3Rpb24oYSl7bGMoYSxnKX0pfXVjKGEpO2MmJigoYj10aGlzJiYic2xvdCI9PT10aGlzLmxvY2FsTmFtZSkmJihmPSEwKSwoZHx8YikmJkFjKGMpKTtmfHwoZj16KHRoaXMpP3RoaXMuaG9zdDp0aGlzLCghZS5yb290JiYic2xvdCIhPT1hLmxvY2FsTmFtZXx8Zj09PWEuX19zaGFkeV9uYXRpdmVfcGFyZW50Tm9kZSkmJmYuX19zaGFkeV9uYXRpdmVfcmVtb3ZlQ2hpbGQoYSkpO3djKHRoaXMsbnVsbCxhKTtyZXR1cm4gYX0scmVwbGFjZUNoaWxkOmZ1bmN0aW9uKGEsYil7dGhpcy5fX3NoYWR5X2luc2VydEJlZm9yZShhLGIpO3RoaXMuX19zaGFkeV9yZW1vdmVDaGlsZChiKTtyZXR1cm4gYX0sY2xvbmVOb2RlOmZ1bmN0aW9uKGEpe2lmKCJ0ZW1wbGF0ZSI9PXRoaXMubG9jYWxOYW1lKXJldHVybiB0aGlzLl9fc2hhZHlfbmF0aXZlX2Nsb25lTm9kZShhKTsKdmFyIGI9dGhpcy5fX3NoYWR5X25hdGl2ZV9jbG9uZU5vZGUoITEpO2lmKGEmJmIubm9kZVR5cGUhPT1Ob2RlLkFUVFJJQlVURV9OT0RFKXthPXRoaXMuX19zaGFkeV9jaGlsZE5vZGVzO2Zvcih2YXIgYz0wLGQ7YzxhLmxlbmd0aDtjKyspZD1hW2NdLl9fc2hhZHlfY2xvbmVOb2RlKCEwKSxiLl9fc2hhZHlfYXBwZW5kQ2hpbGQoZCl9cmV0dXJuIGJ9LGdldFJvb3ROb2RlOmZ1bmN0aW9uKGEpe2lmKHRoaXMmJnRoaXMubm9kZVR5cGUpe3ZhciBiPXUodGhpcyksYz1iLks7dm9pZCAwPT09YyYmKHoodGhpcyk/KGM9dGhpcyxiLks9Yyk6KGM9KGM9dGhpcy5fX3NoYWR5X3BhcmVudE5vZGUpP2MuX19zaGFkeV9nZXRSb290Tm9kZShhKTp0aGlzLGRvY3VtZW50LmRvY3VtZW50RWxlbWVudC5fX3NoYWR5X25hdGl2ZV9jb250YWlucyh0aGlzKSYmKGIuSz1jKSkpO3JldHVybiBjfX0sY29udGFpbnM6ZnVuY3Rpb24oYSl7cmV0dXJuIFVhKHRoaXMsYSl9fSk7ZnVuY3Rpb24gRGMoYSxiLGMpe3ZhciBkPVtdO0VjKGEuX19zaGFkeV9jaGlsZE5vZGVzLGIsYyxkKTtyZXR1cm4gZH1mdW5jdGlvbiBFYyhhLGIsYyxkKXtmb3IodmFyIGU9MCxmPWEubGVuZ3RoLGc9dm9pZCAwO2U8ZiYmKGc9YVtlXSk7ZSsrKXt2YXIgaDtpZihoPWcubm9kZVR5cGU9PT1Ob2RlLkVMRU1FTlRfTk9ERSl7aD1nO3ZhciBrPWIsbD1jLG09ZCxxPWsoaCk7cSYmbS5wdXNoKGgpO2wmJmwocSk/aD1xOihFYyhoLl9fc2hhZHlfY2hpbGROb2RlcyxrLGwsbSksaD12b2lkIDApfWlmKGgpYnJlYWt9fQp2YXIgRmM9Qih7Z2V0IGZpcnN0RWxlbWVudENoaWxkKCl7dmFyIGE9dih0aGlzKTtpZihhJiZ2b2lkIDAhPT1hLmZpcnN0Q2hpbGQpe2ZvcihhPXRoaXMuX19zaGFkeV9maXJzdENoaWxkO2EmJmEubm9kZVR5cGUhPT1Ob2RlLkVMRU1FTlRfTk9ERTspYT1hLl9fc2hhZHlfbmV4dFNpYmxpbmc7cmV0dXJuIGF9cmV0dXJuIHRoaXMuX19zaGFkeV9uYXRpdmVfZmlyc3RFbGVtZW50Q2hpbGR9LGdldCBsYXN0RWxlbWVudENoaWxkKCl7dmFyIGE9dih0aGlzKTtpZihhJiZ2b2lkIDAhPT1hLmxhc3RDaGlsZCl7Zm9yKGE9dGhpcy5fX3NoYWR5X2xhc3RDaGlsZDthJiZhLm5vZGVUeXBlIT09Tm9kZS5FTEVNRU5UX05PREU7KWE9YS5fX3NoYWR5X3ByZXZpb3VzU2libGluZztyZXR1cm4gYX1yZXR1cm4gdGhpcy5fX3NoYWR5X25hdGl2ZV9sYXN0RWxlbWVudENoaWxkfSxnZXQgY2hpbGRyZW4oKXtyZXR1cm4geSh0aGlzKT9WYShBcnJheS5wcm90b3R5cGUuZmlsdGVyLmNhbGwodGhpcy5fX3NoYWR5X2NoaWxkTm9kZXMsCmZ1bmN0aW9uKGEpe3JldHVybiBhLm5vZGVUeXBlPT09Tm9kZS5FTEVNRU5UX05PREV9KSk6dGhpcy5fX3NoYWR5X25hdGl2ZV9jaGlsZHJlbn0sZ2V0IGNoaWxkRWxlbWVudENvdW50KCl7dmFyIGE9dGhpcy5fX3NoYWR5X2NoaWxkcmVuO3JldHVybiBhP2EubGVuZ3RoOjB9fSksR2M9Qih7cXVlcnlTZWxlY3RvcjpmdW5jdGlvbihhKXtyZXR1cm4gRGModGhpcyxmdW5jdGlvbihiKXtyZXR1cm4gT2EuY2FsbChiLGEpfSxmdW5jdGlvbihhKXtyZXR1cm4hIWF9KVswXXx8bnVsbH0scXVlcnlTZWxlY3RvckFsbDpmdW5jdGlvbihhLGIpe2lmKGIpe2I9QXJyYXkucHJvdG90eXBlLnNsaWNlLmNhbGwodGhpcy5fX3NoYWR5X25hdGl2ZV9xdWVyeVNlbGVjdG9yQWxsKGEpKTt2YXIgYz10aGlzLl9fc2hhZHlfZ2V0Um9vdE5vZGUoKTtyZXR1cm4gYi5maWx0ZXIoZnVuY3Rpb24oYSl7cmV0dXJuIGEuX19zaGFkeV9nZXRSb290Tm9kZSgpPT1jfSl9cmV0dXJuIERjKHRoaXMsZnVuY3Rpb24oYil7cmV0dXJuIE9hLmNhbGwoYiwKYSl9KX19KSxIYz13Lm9hP09iamVjdC5hc3NpZ24oe30sRmMpOkZjO09iamVjdC5hc3NpZ24oRmMsR2MpO3ZhciBJYz1CKHtnZXRFbGVtZW50QnlJZDpmdW5jdGlvbihhKXtyZXR1cm4iIj09PWE/bnVsbDpEYyh0aGlzLGZ1bmN0aW9uKGIpe3JldHVybiBiLmlkPT1hfSxmdW5jdGlvbihhKXtyZXR1cm4hIWF9KVswXXx8bnVsbH19KTt2YXIgSmM9Qih7Z2V0IGFjdGl2ZUVsZW1lbnQoKXt2YXIgYT13Lm0/ZG9jdW1lbnQuX19zaGFkeV9uYXRpdmVfYWN0aXZlRWxlbWVudDpkb2N1bWVudC5hY3RpdmVFbGVtZW50O2lmKCFhfHwhYS5ub2RlVHlwZSlyZXR1cm4gbnVsbDt2YXIgYj0hIXoodGhpcyk7aWYoISh0aGlzPT09ZG9jdW1lbnR8fGImJnRoaXMuaG9zdCE9PWEmJnRoaXMuaG9zdC5fX3NoYWR5X25hdGl2ZV9jb250YWlucyhhKSkpcmV0dXJuIG51bGw7Zm9yKGI9eGMoYSk7YiYmYiE9PXRoaXM7KWE9Yi5ob3N0LGI9eGMoYSk7cmV0dXJuIHRoaXM9PT1kb2N1bWVudD9iP251bGw6YTpiPT09dGhpcz9hOm51bGx9fSk7dmFyIEtjPWRvY3VtZW50LmltcGxlbWVudGF0aW9uLmNyZWF0ZUhUTUxEb2N1bWVudCgiaW5lcnQiKSxMYz1CKHtnZXQgaW5uZXJIVE1MKCl7cmV0dXJuIHkodGhpcyk/a2IoInRlbXBsYXRlIj09PXRoaXMubG9jYWxOYW1lP3RoaXMuY29udGVudDp0aGlzLGZ1bmN0aW9uKGEpe3JldHVybiBhLl9fc2hhZHlfY2hpbGROb2Rlc30pOnRoaXMuX19zaGFkeV9uYXRpdmVfaW5uZXJIVE1MfSxzZXQgaW5uZXJIVE1MKGEpe2lmKCJ0ZW1wbGF0ZSI9PT10aGlzLmxvY2FsTmFtZSl0aGlzLl9fc2hhZHlfbmF0aXZlX2lubmVySFRNTD1hO2Vsc2V7dGModGhpcyk7dmFyIGI9dGhpcy5sb2NhbE5hbWV8fCJkaXYiO2I9dGhpcy5uYW1lc3BhY2VVUkkmJnRoaXMubmFtZXNwYWNlVVJJIT09S2MubmFtZXNwYWNlVVJJP0tjLmNyZWF0ZUVsZW1lbnROUyh0aGlzLm5hbWVzcGFjZVVSSSxiKTpLYy5jcmVhdGVFbGVtZW50KGIpO2Zvcih3Lm0/Yi5fX3NoYWR5X25hdGl2ZV9pbm5lckhUTUw9YTpiLmlubmVySFRNTD0KYTthPWIuX19zaGFkeV9maXJzdENoaWxkOyl0aGlzLl9fc2hhZHlfaW5zZXJ0QmVmb3JlKGEpfX19KTt2YXIgTWM9Qih7YWRkRXZlbnRMaXN0ZW5lcjpmdW5jdGlvbihhLGIsYyl7Im9iamVjdCIhPT10eXBlb2YgYyYmKGM9e2NhcHR1cmU6ISFjfSk7Yy4kPXRoaXM7dGhpcy5ob3N0Ll9fc2hhZHlfYWRkRXZlbnRMaXN0ZW5lcihhLGIsYyl9LHJlbW92ZUV2ZW50TGlzdGVuZXI6ZnVuY3Rpb24oYSxiLGMpeyJvYmplY3QiIT09dHlwZW9mIGMmJihjPXtjYXB0dXJlOiEhY30pO2MuJD10aGlzO3RoaXMuaG9zdC5fX3NoYWR5X3JlbW92ZUV2ZW50TGlzdGVuZXIoYSxiLGMpfX0pO2Z1bmN0aW9uIE5jKGEsYil7QShhLE1jLGIpO0EoYSxKYyxiKTtBKGEsTGMsYik7QShhLEZjLGIpO3cuSiYmIWI/KEEoYSxDYyxiKSxBKGEsSWMsYikpOncubXx8KEEoYSxBYiksQShhLHpiKSl9O3ZhciBPYz17fSxQYz13LmRlZmVyQ29ubmVjdGlvbkNhbGxiYWNrcyYmImxvYWRpbmciPT09ZG9jdW1lbnQucmVhZHlTdGF0ZSxRYztmdW5jdGlvbiBSYyhhKXt2YXIgYj1bXTtkbyBiLnVuc2hpZnQoYSk7d2hpbGUoYT1hLl9fc2hhZHlfcGFyZW50Tm9kZSk7cmV0dXJuIGJ9CmZ1bmN0aW9uIFNjKGEsYixjKXtpZihhIT09T2MpdGhyb3cgbmV3IFR5cGVFcnJvcigiSWxsZWdhbCBjb25zdHJ1Y3RvciIpO3RoaXMuemE9IlNoYWR5Um9vdCI7dGhpcy5ob3N0PWI7dGhpcy5tb2RlPWMmJmMubW9kZTtqYyhiKTthPXUoYik7YS5yb290PXRoaXM7YS5wYT0iY2xvc2VkIiE9PXRoaXMubW9kZT90aGlzOm51bGw7YT11KHRoaXMpO2EuZmlyc3RDaGlsZD1hLmxhc3RDaGlsZD1hLnBhcmVudE5vZGU9YS5uZXh0U2libGluZz1hLnByZXZpb3VzU2libGluZz1udWxsO2EuY2hpbGROb2Rlcz1bXTt0aGlzLmJhPXRoaXMuQj0hMTt0aGlzLmM9dGhpcy5iPXRoaXMuYT1udWxsO2lmKHcucHJlZmVyUGVyZm9ybWFuY2UpZm9yKDthPWIuX19zaGFkeV9uYXRpdmVfZmlyc3RDaGlsZDspYi5fX3NoYWR5X25hdGl2ZV9yZW1vdmVDaGlsZChhKTtlbHNlIEFjKHRoaXMpfWZ1bmN0aW9uIEFjKGEpe2EuQnx8KGEuQj0hMCxZYShmdW5jdGlvbigpe3JldHVybiBUYyhhKX0pKX0KZnVuY3Rpb24gVGMoYSl7dmFyIGI7aWYoYj1hLkIpe2Zvcih2YXIgYzthOylhOnthLkImJihjPWEpLGI9YTthPWIuaG9zdC5fX3NoYWR5X2dldFJvb3ROb2RlKCk7aWYoeihhKSYmKGI9dihiLmhvc3QpKSYmMDxiLk4pYnJlYWsgYTthPXZvaWQgMH1iPWN9KGM9YikmJmMuX3JlbmRlclNlbGYoKX0KU2MucHJvdG90eXBlLl9yZW5kZXJTZWxmPWZ1bmN0aW9uKCl7dmFyIGE9UGM7UGM9ITA7dGhpcy5CPSExO2lmKHRoaXMuYSl7VWModGhpcyk7Zm9yKHZhciBiPTAsYztiPHRoaXMuYS5sZW5ndGg7YisrKXtjPXRoaXMuYVtiXTt2YXIgZD12KGMpLGU9ZC5hc3NpZ25lZE5vZGVzO2QuYXNzaWduZWROb2Rlcz1bXTtkLkQ9W107aWYoZC5jYT1lKWZvcihkPTA7ZDxlLmxlbmd0aDtkKyspe3ZhciBmPXYoZVtkXSk7Zi5QPWYuYXNzaWduZWRTbG90O2YuYXNzaWduZWRTbG90PT09YyYmKGYuYXNzaWduZWRTbG90PW51bGwpfX1mb3IoYj10aGlzLmhvc3QuX19zaGFkeV9maXJzdENoaWxkO2I7Yj1iLl9fc2hhZHlfbmV4dFNpYmxpbmcpVmModGhpcyxiKTtmb3IoYj0wO2I8dGhpcy5hLmxlbmd0aDtiKyspe2M9dGhpcy5hW2JdO2U9dihjKTtpZighZS5hc3NpZ25lZE5vZGVzLmxlbmd0aClmb3IoZD1jLl9fc2hhZHlfZmlyc3RDaGlsZDtkO2Q9ZC5fX3NoYWR5X25leHRTaWJsaW5nKVZjKHRoaXMsCmQsYyk7KGQ9KGQ9dihjLl9fc2hhZHlfcGFyZW50Tm9kZSkpJiZkLnJvb3QpJiYoTWEoZCl8fGQuQikmJmQuX3JlbmRlclNlbGYoKTtXYyh0aGlzLGUuRCxlLmFzc2lnbmVkTm9kZXMpO2lmKGQ9ZS5jYSl7Zm9yKGY9MDtmPGQubGVuZ3RoO2YrKyl2KGRbZl0pLlA9bnVsbDtlLmNhPW51bGw7ZC5sZW5ndGg+ZS5hc3NpZ25lZE5vZGVzLmxlbmd0aCYmKGUuVD0hMCl9ZS5UJiYoZS5UPSExLFhjKHRoaXMsYykpfWM9dGhpcy5hO2I9W107Zm9yKGU9MDtlPGMubGVuZ3RoO2UrKylkPWNbZV0uX19zaGFkeV9wYXJlbnROb2RlLChmPXYoZCkpJiZmLnJvb3R8fCEoMD5iLmluZGV4T2YoZCkpfHxiLnB1c2goZCk7Zm9yKGM9MDtjPGIubGVuZ3RoO2MrKyl7Zj1iW2NdO2U9Zj09PXRoaXM/dGhpcy5ob3N0OmY7ZD1bXTtmPWYuX19zaGFkeV9jaGlsZE5vZGVzO2Zvcih2YXIgZz0wO2c8Zi5sZW5ndGg7ZysrKXt2YXIgaD1mW2ddO2lmKCJzbG90Ij09aC5sb2NhbE5hbWUpe2g9dihoKS5EO2Zvcih2YXIgaz0KMDtrPGgubGVuZ3RoO2srKylkLnB1c2goaFtrXSl9ZWxzZSBkLnB1c2goaCl9Zj1BcnJheS5wcm90b3R5cGUuc2xpY2UuY2FsbChlLl9fc2hhZHlfbmF0aXZlX2NoaWxkTm9kZXMpO2c9ZWMoZCxkLmxlbmd0aCxmLGYubGVuZ3RoKTtrPWg9MDtmb3IodmFyIGw9dm9pZCAwO2g8Zy5sZW5ndGgmJihsPWdbaF0pO2grKyl7Zm9yKHZhciBtPTAscT12b2lkIDA7bTxsLkwubGVuZ3RoJiYocT1sLkxbbV0pO20rKylxLl9fc2hhZHlfbmF0aXZlX3BhcmVudE5vZGU9PT1lJiZlLl9fc2hhZHlfbmF0aXZlX3JlbW92ZUNoaWxkKHEpLGYuc3BsaWNlKGwuaW5kZXgraywxKTtrLT1sLlJ9az0wO2ZvcihsPXZvaWQgMDtrPGcubGVuZ3RoJiYobD1nW2tdKTtrKyspZm9yKGg9ZltsLmluZGV4XSxtPWwuaW5kZXg7bTxsLmluZGV4K2wuUjttKyspcT1kW21dLGUuX19zaGFkeV9uYXRpdmVfaW5zZXJ0QmVmb3JlKHEsaCksZi5zcGxpY2UobSwwLHEpfX1pZighdy5wcmVmZXJQZXJmb3JtYW5jZSYmIXRoaXMuYmEpZm9yKGI9CnRoaXMuaG9zdC5fX3NoYWR5X2NoaWxkTm9kZXMsYz0wLGU9Yi5sZW5ndGg7YzxlO2MrKylkPWJbY10sZj12KGQpLGQuX19zaGFkeV9uYXRpdmVfcGFyZW50Tm9kZSE9PXRoaXMuaG9zdHx8InNsb3QiIT09ZC5sb2NhbE5hbWUmJmYuYXNzaWduZWRTbG90fHx0aGlzLmhvc3QuX19zaGFkeV9uYXRpdmVfcmVtb3ZlQ2hpbGQoZCk7dGhpcy5iYT0hMDtQYz1hO1FjJiZRYygpfTtmdW5jdGlvbiBWYyhhLGIsYyl7dmFyIGQ9dShiKSxlPWQuUDtkLlA9bnVsbDtjfHwoYz0oYT1hLmJbYi5fX3NoYWR5X3Nsb3R8fCJfX2NhdGNoYWxsIl0pJiZhWzBdKTtjPyh1KGMpLmFzc2lnbmVkTm9kZXMucHVzaChiKSxkLmFzc2lnbmVkU2xvdD1jKTpkLmFzc2lnbmVkU2xvdD12b2lkIDA7ZSE9PWQuYXNzaWduZWRTbG90JiZkLmFzc2lnbmVkU2xvdCYmKHUoZC5hc3NpZ25lZFNsb3QpLlQ9ITApfQpmdW5jdGlvbiBXYyhhLGIsYyl7Zm9yKHZhciBkPTAsZT12b2lkIDA7ZDxjLmxlbmd0aCYmKGU9Y1tkXSk7ZCsrKWlmKCJzbG90Ij09ZS5sb2NhbE5hbWUpe3ZhciBmPXYoZSkuYXNzaWduZWROb2RlcztmJiZmLmxlbmd0aCYmV2MoYSxiLGYpfWVsc2UgYi5wdXNoKGNbZF0pfWZ1bmN0aW9uIFhjKGEsYil7Yi5fX3NoYWR5X25hdGl2ZV9kaXNwYXRjaEV2ZW50KG5ldyBFdmVudCgic2xvdGNoYW5nZSIpKTtiPXYoYik7Yi5hc3NpZ25lZFNsb3QmJlhjKGEsYi5hc3NpZ25lZFNsb3QpfQpmdW5jdGlvbiBVYyhhKXtpZihhLmMmJmEuYy5sZW5ndGgpe2Zvcih2YXIgYj1hLmMsYyxkPTA7ZDxiLmxlbmd0aDtkKyspe3ZhciBlPWJbZF07amMoZSk7dmFyIGY9ZS5fX3NoYWR5X3BhcmVudE5vZGU7amMoZik7Zj12KGYpO2YuTj0oZi5OfHwwKSsxO2Y9WWMoZSk7YS5iW2ZdPyhjPWN8fHt9LGNbZl09ITAsYS5iW2ZdLnB1c2goZSkpOmEuYltmXT1bZV07YS5hLnB1c2goZSl9aWYoYylmb3IodmFyIGcgaW4gYylhLmJbZ109WmMoYS5iW2ddKTthLmM9W119fWZ1bmN0aW9uIFljKGEpe3ZhciBiPWEubmFtZXx8YS5nZXRBdHRyaWJ1dGUoIm5hbWUiKXx8Il9fY2F0Y2hhbGwiO3JldHVybiBhLndhPWJ9CmZ1bmN0aW9uIFpjKGEpe3JldHVybiBhLnNvcnQoZnVuY3Rpb24oYSxjKXthPVJjKGEpO2Zvcih2YXIgYj1SYyhjKSxlPTA7ZTxhLmxlbmd0aDtlKyspe2M9YVtlXTt2YXIgZj1iW2VdO2lmKGMhPT1mKXJldHVybiBhPUFycmF5LmZyb20oYy5fX3NoYWR5X3BhcmVudE5vZGUuX19zaGFkeV9jaGlsZE5vZGVzKSxhLmluZGV4T2YoYyktYS5pbmRleE9mKGYpfX0pfQpmdW5jdGlvbiBCYyhhLGIpe2lmKGEuYSl7VWMoYSk7dmFyIGM9YS5iLGQ7Zm9yKGQgaW4gYylmb3IodmFyIGU9Y1tkXSxmPTA7ZjxlLmxlbmd0aDtmKyspe3ZhciBnPWVbZl07aWYoVWEoYixnKSl7ZS5zcGxpY2UoZiwxKTt2YXIgaD1hLmEuaW5kZXhPZihnKTswPD1oJiYoYS5hLnNwbGljZShoLDEpLChoPXYoZy5fX3NoYWR5X3BhcmVudE5vZGUpKSYmaC5OJiZoLk4tLSk7Zi0tO2c9dihnKTtpZihoPWcuRClmb3IodmFyIGs9MDtrPGgubGVuZ3RoO2srKyl7dmFyIGw9aFtrXSxtPWwuX19zaGFkeV9uYXRpdmVfcGFyZW50Tm9kZTttJiZtLl9fc2hhZHlfbmF0aXZlX3JlbW92ZUNoaWxkKGwpfWcuRD1bXTtnLmFzc2lnbmVkTm9kZXM9W107aD0hMH19cmV0dXJuIGh9fWZ1bmN0aW9uIE1hKGEpe1VjKGEpO3JldHVybiEoIWEuYXx8IWEuYS5sZW5ndGgpfQooZnVuY3Rpb24oYSl7YS5fX3Byb3RvX189RG9jdW1lbnRGcmFnbWVudC5wcm90b3R5cGU7TmMoYSwiX19zaGFkeV8iKTtOYyhhKTtPYmplY3QuZGVmaW5lUHJvcGVydGllcyhhLHtub2RlVHlwZTp7dmFsdWU6Tm9kZS5ET0NVTUVOVF9GUkFHTUVOVF9OT0RFLGNvbmZpZ3VyYWJsZTohMH0sbm9kZU5hbWU6e3ZhbHVlOiIjZG9jdW1lbnQtZnJhZ21lbnQiLGNvbmZpZ3VyYWJsZTohMH0sbm9kZVZhbHVlOnt2YWx1ZTpudWxsLGNvbmZpZ3VyYWJsZTohMH19KTtbImxvY2FsTmFtZSIsIm5hbWVzcGFjZVVSSSIsInByZWZpeCJdLmZvckVhY2goZnVuY3Rpb24oYil7T2JqZWN0LmRlZmluZVByb3BlcnR5KGEsYix7dmFsdWU6dm9pZCAwLGNvbmZpZ3VyYWJsZTohMH0pfSk7WyJvd25lckRvY3VtZW50IiwiYmFzZVVSSSIsImlzQ29ubmVjdGVkIl0uZm9yRWFjaChmdW5jdGlvbihiKXtPYmplY3QuZGVmaW5lUHJvcGVydHkoYSxiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5ob3N0W2JdfSwKY29uZmlndXJhYmxlOiEwfSl9KX0pKFNjLnByb3RvdHlwZSk7CmlmKHdpbmRvdy5jdXN0b21FbGVtZW50cyYmdy5lYSYmIXcucHJlZmVyUGVyZm9ybWFuY2Upe3ZhciAkYz1uZXcgTWFwO1FjPWZ1bmN0aW9uKCl7dmFyIGE9W107JGMuZm9yRWFjaChmdW5jdGlvbihiLGMpe2EucHVzaChbYyxiXSl9KTskYy5jbGVhcigpO2Zvcih2YXIgYj0wO2I8YS5sZW5ndGg7YisrKXt2YXIgYz1hW2JdWzBdO2FbYl1bMV0/Yy51YSgpOmMudmEoKX19O1BjJiZkb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCJyZWFkeXN0YXRlY2hhbmdlIixmdW5jdGlvbigpe1BjPSExO1FjKCl9LHtvbmNlOiEwfSk7dmFyIGFkPWZ1bmN0aW9uKGEsYixjKXt2YXIgZD0wLGU9Il9faXNDb25uZWN0ZWQiK2QrKztpZihifHxjKWEucHJvdG90eXBlLmNvbm5lY3RlZENhbGxiYWNrPWEucHJvdG90eXBlLnVhPWZ1bmN0aW9uKCl7UGM/JGMuc2V0KHRoaXMsITApOnRoaXNbZV18fCh0aGlzW2VdPSEwLGImJmIuY2FsbCh0aGlzKSl9LGEucHJvdG90eXBlLmRpc2Nvbm5lY3RlZENhbGxiYWNrPQphLnByb3RvdHlwZS52YT1mdW5jdGlvbigpe1BjP3RoaXMuaXNDb25uZWN0ZWR8fCRjLnNldCh0aGlzLCExKTp0aGlzW2VdJiYodGhpc1tlXT0hMSxjJiZjLmNhbGwodGhpcykpfTtyZXR1cm4gYX0sYmQ9d2luZG93LmN1c3RvbUVsZW1lbnRzLmRlZmluZTtPYmplY3QuZGVmaW5lUHJvcGVydHkod2luZG93LkN1c3RvbUVsZW1lbnRSZWdpc3RyeS5wcm90b3R5cGUsImRlZmluZSIse3ZhbHVlOmZ1bmN0aW9uKGEsYil7dmFyIGM9Yi5wcm90b3R5cGUuY29ubmVjdGVkQ2FsbGJhY2ssZD1iLnByb3RvdHlwZS5kaXNjb25uZWN0ZWRDYWxsYmFjaztiZC5jYWxsKHdpbmRvdy5jdXN0b21FbGVtZW50cyxhLGFkKGIsYyxkKSk7Yi5wcm90b3R5cGUuY29ubmVjdGVkQ2FsbGJhY2s9YztiLnByb3RvdHlwZS5kaXNjb25uZWN0ZWRDYWxsYmFjaz1kfX0pfWZ1bmN0aW9uIHhjKGEpe2E9YS5fX3NoYWR5X2dldFJvb3ROb2RlKCk7aWYoeihhKSlyZXR1cm4gYX07ZnVuY3Rpb24gY2QoYSl7dGhpcy5ub2RlPWF9bj1jZC5wcm90b3R5cGU7bi5hZGRFdmVudExpc3RlbmVyPWZ1bmN0aW9uKGEsYixjKXtyZXR1cm4gdGhpcy5ub2RlLl9fc2hhZHlfYWRkRXZlbnRMaXN0ZW5lcihhLGIsYyl9O24ucmVtb3ZlRXZlbnRMaXN0ZW5lcj1mdW5jdGlvbihhLGIsYyl7cmV0dXJuIHRoaXMubm9kZS5fX3NoYWR5X3JlbW92ZUV2ZW50TGlzdGVuZXIoYSxiLGMpfTtuLmFwcGVuZENoaWxkPWZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLm5vZGUuX19zaGFkeV9hcHBlbmRDaGlsZChhKX07bi5pbnNlcnRCZWZvcmU9ZnVuY3Rpb24oYSxiKXtyZXR1cm4gdGhpcy5ub2RlLl9fc2hhZHlfaW5zZXJ0QmVmb3JlKGEsYil9O24ucmVtb3ZlQ2hpbGQ9ZnVuY3Rpb24oYSl7cmV0dXJuIHRoaXMubm9kZS5fX3NoYWR5X3JlbW92ZUNoaWxkKGEpfTtuLnJlcGxhY2VDaGlsZD1mdW5jdGlvbihhLGIpe3JldHVybiB0aGlzLm5vZGUuX19zaGFkeV9yZXBsYWNlQ2hpbGQoYSxiKX07Cm4uY2xvbmVOb2RlPWZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLm5vZGUuX19zaGFkeV9jbG9uZU5vZGUoYSl9O24uZ2V0Um9vdE5vZGU9ZnVuY3Rpb24oYSl7cmV0dXJuIHRoaXMubm9kZS5fX3NoYWR5X2dldFJvb3ROb2RlKGEpfTtuLmNvbnRhaW5zPWZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLm5vZGUuX19zaGFkeV9jb250YWlucyhhKX07bi5kaXNwYXRjaEV2ZW50PWZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLm5vZGUuX19zaGFkeV9kaXNwYXRjaEV2ZW50KGEpfTtuLnNldEF0dHJpYnV0ZT1mdW5jdGlvbihhLGIpe3RoaXMubm9kZS5fX3NoYWR5X3NldEF0dHJpYnV0ZShhLGIpfTtuLmdldEF0dHJpYnV0ZT1mdW5jdGlvbihhKXtyZXR1cm4gdGhpcy5ub2RlLl9fc2hhZHlfbmF0aXZlX2dldEF0dHJpYnV0ZShhKX07bi5oYXNBdHRyaWJ1dGU9ZnVuY3Rpb24oYSl7cmV0dXJuIHRoaXMubm9kZS5fX3NoYWR5X25hdGl2ZV9oYXNBdHRyaWJ1dGUoYSl9O24ucmVtb3ZlQXR0cmlidXRlPWZ1bmN0aW9uKGEpe3RoaXMubm9kZS5fX3NoYWR5X3JlbW92ZUF0dHJpYnV0ZShhKX07Cm4uYXR0YWNoU2hhZG93PWZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLm5vZGUuX19zaGFkeV9hdHRhY2hTaGFkb3coYSl9O24uZm9jdXM9ZnVuY3Rpb24oKXt0aGlzLm5vZGUuX19zaGFkeV9uYXRpdmVfZm9jdXMoKX07bi5ibHVyPWZ1bmN0aW9uKCl7dGhpcy5ub2RlLl9fc2hhZHlfYmx1cigpfTtuLmltcG9ydE5vZGU9ZnVuY3Rpb24oYSxiKXtpZih0aGlzLm5vZGUubm9kZVR5cGU9PT1Ob2RlLkRPQ1VNRU5UX05PREUpcmV0dXJuIHRoaXMubm9kZS5fX3NoYWR5X2ltcG9ydE5vZGUoYSxiKX07bi5nZXRFbGVtZW50QnlJZD1mdW5jdGlvbihhKXtpZih0aGlzLm5vZGUubm9kZVR5cGU9PT1Ob2RlLkRPQ1VNRU5UX05PREUpcmV0dXJuIHRoaXMubm9kZS5fX3NoYWR5X2dldEVsZW1lbnRCeUlkKGEpfTtuLnF1ZXJ5U2VsZWN0b3I9ZnVuY3Rpb24oYSl7cmV0dXJuIHRoaXMubm9kZS5fX3NoYWR5X3F1ZXJ5U2VsZWN0b3IoYSl9OwpuLnF1ZXJ5U2VsZWN0b3JBbGw9ZnVuY3Rpb24oYSxiKXtyZXR1cm4gdGhpcy5ub2RlLl9fc2hhZHlfcXVlcnlTZWxlY3RvckFsbChhLGIpfTtuLmFzc2lnbmVkTm9kZXM9ZnVuY3Rpb24oYSl7aWYoInNsb3QiPT09dGhpcy5ub2RlLmxvY2FsTmFtZSlyZXR1cm4gdGhpcy5ub2RlLl9fc2hhZHlfYXNzaWduZWROb2RlcyhhKX07CnAuT2JqZWN0LmRlZmluZVByb3BlcnRpZXMoY2QucHJvdG90eXBlLHthY3RpdmVFbGVtZW50Ontjb25maWd1cmFibGU6ITAsZW51bWVyYWJsZTohMCxnZXQ6ZnVuY3Rpb24oKXtpZih6KHRoaXMubm9kZSl8fHRoaXMubm9kZS5ub2RlVHlwZT09PU5vZGUuRE9DVU1FTlRfTk9ERSlyZXR1cm4gdGhpcy5ub2RlLl9fc2hhZHlfYWN0aXZlRWxlbWVudH19LF9hY3RpdmVFbGVtZW50Ontjb25maWd1cmFibGU6ITAsZW51bWVyYWJsZTohMCxnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5hY3RpdmVFbGVtZW50fX0saG9zdDp7Y29uZmlndXJhYmxlOiEwLGVudW1lcmFibGU6ITAsZ2V0OmZ1bmN0aW9uKCl7aWYoeih0aGlzLm5vZGUpKXJldHVybiB0aGlzLm5vZGUuaG9zdH19LHBhcmVudE5vZGU6e2NvbmZpZ3VyYWJsZTohMCxlbnVtZXJhYmxlOiEwLGdldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLm5vZGUuX19zaGFkeV9wYXJlbnROb2RlfX0sZmlyc3RDaGlsZDp7Y29uZmlndXJhYmxlOiEwLAplbnVtZXJhYmxlOiEwLGdldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLm5vZGUuX19zaGFkeV9maXJzdENoaWxkfX0sbGFzdENoaWxkOntjb25maWd1cmFibGU6ITAsZW51bWVyYWJsZTohMCxnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5ub2RlLl9fc2hhZHlfbGFzdENoaWxkfX0sbmV4dFNpYmxpbmc6e2NvbmZpZ3VyYWJsZTohMCxlbnVtZXJhYmxlOiEwLGdldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLm5vZGUuX19zaGFkeV9uZXh0U2libGluZ319LHByZXZpb3VzU2libGluZzp7Y29uZmlndXJhYmxlOiEwLGVudW1lcmFibGU6ITAsZ2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMubm9kZS5fX3NoYWR5X3ByZXZpb3VzU2libGluZ319LGNoaWxkTm9kZXM6e2NvbmZpZ3VyYWJsZTohMCxlbnVtZXJhYmxlOiEwLGdldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLm5vZGUuX19zaGFkeV9jaGlsZE5vZGVzfX0scGFyZW50RWxlbWVudDp7Y29uZmlndXJhYmxlOiEwLGVudW1lcmFibGU6ITAsCmdldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLm5vZGUuX19zaGFkeV9wYXJlbnRFbGVtZW50fX0sZmlyc3RFbGVtZW50Q2hpbGQ6e2NvbmZpZ3VyYWJsZTohMCxlbnVtZXJhYmxlOiEwLGdldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLm5vZGUuX19zaGFkeV9maXJzdEVsZW1lbnRDaGlsZH19LGxhc3RFbGVtZW50Q2hpbGQ6e2NvbmZpZ3VyYWJsZTohMCxlbnVtZXJhYmxlOiEwLGdldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLm5vZGUuX19zaGFkeV9sYXN0RWxlbWVudENoaWxkfX0sbmV4dEVsZW1lbnRTaWJsaW5nOntjb25maWd1cmFibGU6ITAsZW51bWVyYWJsZTohMCxnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5ub2RlLl9fc2hhZHlfbmV4dEVsZW1lbnRTaWJsaW5nfX0scHJldmlvdXNFbGVtZW50U2libGluZzp7Y29uZmlndXJhYmxlOiEwLGVudW1lcmFibGU6ITAsZ2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMubm9kZS5fX3NoYWR5X3ByZXZpb3VzRWxlbWVudFNpYmxpbmd9fSwKY2hpbGRyZW46e2NvbmZpZ3VyYWJsZTohMCxlbnVtZXJhYmxlOiEwLGdldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLm5vZGUuX19zaGFkeV9jaGlsZHJlbn19LGNoaWxkRWxlbWVudENvdW50Ontjb25maWd1cmFibGU6ITAsZW51bWVyYWJsZTohMCxnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5ub2RlLl9fc2hhZHlfY2hpbGRFbGVtZW50Q291bnR9fSxzaGFkb3dSb290Ontjb25maWd1cmFibGU6ITAsZW51bWVyYWJsZTohMCxnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5ub2RlLl9fc2hhZHlfc2hhZG93Um9vdH19LGFzc2lnbmVkU2xvdDp7Y29uZmlndXJhYmxlOiEwLGVudW1lcmFibGU6ITAsZ2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMubm9kZS5fX3NoYWR5X2Fzc2lnbmVkU2xvdH19LGlzQ29ubmVjdGVkOntjb25maWd1cmFibGU6ITAsZW51bWVyYWJsZTohMCxnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5ub2RlLl9fc2hhZHlfaXNDb25uZWN0ZWR9fSxpbm5lckhUTUw6e2NvbmZpZ3VyYWJsZTohMCwKZW51bWVyYWJsZTohMCxnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5ub2RlLl9fc2hhZHlfaW5uZXJIVE1MfSxzZXQ6ZnVuY3Rpb24oYSl7dGhpcy5ub2RlLl9fc2hhZHlfaW5uZXJIVE1MPWF9fSx0ZXh0Q29udGVudDp7Y29uZmlndXJhYmxlOiEwLGVudW1lcmFibGU6ITAsZ2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMubm9kZS5fX3NoYWR5X3RleHRDb250ZW50fSxzZXQ6ZnVuY3Rpb24oYSl7dGhpcy5ub2RlLl9fc2hhZHlfdGV4dENvbnRlbnQ9YX19LHNsb3Q6e2NvbmZpZ3VyYWJsZTohMCxlbnVtZXJhYmxlOiEwLGdldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLm5vZGUuX19zaGFkeV9zbG90fSxzZXQ6ZnVuY3Rpb24oYSl7dGhpcy5ub2RlLl9fc2hhZHlfc2xvdD1hfX19KTsKY2MuZm9yRWFjaChmdW5jdGlvbihhKXtPYmplY3QuZGVmaW5lUHJvcGVydHkoY2QucHJvdG90eXBlLGEse2dldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLm5vZGVbIl9fc2hhZHlfIithXX0sc2V0OmZ1bmN0aW9uKGIpe3RoaXMubm9kZVsiX19zaGFkeV8iK2FdPWJ9LGNvbmZpZ3VyYWJsZTohMH0pfSk7dmFyIGRkPW5ldyBXZWFrTWFwO2Z1bmN0aW9uIGVkKGEpe2lmKHooYSl8fGEgaW5zdGFuY2VvZiBjZClyZXR1cm4gYTt2YXIgYj1kZC5nZXQoYSk7Ynx8KGI9bmV3IGNkKGEpLGRkLnNldChhLGIpKTtyZXR1cm4gYn07dmFyIGZkPUIoe2Rpc3BhdGNoRXZlbnQ6ZnVuY3Rpb24oYSl7WmEoKTtyZXR1cm4gdGhpcy5fX3NoYWR5X25hdGl2ZV9kaXNwYXRjaEV2ZW50KGEpfSxhZGRFdmVudExpc3RlbmVyOlViLHJlbW92ZUV2ZW50TGlzdGVuZXI6V2J9KTt2YXIgZ2Q9Qih7Z2V0IGFzc2lnbmVkU2xvdCgpe3ZhciBhPXRoaXMuX19zaGFkeV9wYXJlbnROb2RlOyhhPWEmJmEuX19zaGFkeV9zaGFkb3dSb290KSYmVGMoYSk7cmV0dXJuKGE9dih0aGlzKSkmJmEuYXNzaWduZWRTbG90fHxudWxsfX0pO3ZhciBoZD13aW5kb3cuZG9jdW1lbnQ7ZnVuY3Rpb24gaWQoYSxiKXtpZigic2xvdCI9PT1iKWE9YS5fX3NoYWR5X3BhcmVudE5vZGUsTGEoYSkmJkFjKHYoYSkucm9vdCk7ZWxzZSBpZigic2xvdCI9PT1hLmxvY2FsTmFtZSYmIm5hbWUiPT09YiYmKGI9eGMoYSkpKXtpZihiLmEpe1VjKGIpO3ZhciBjPWEud2EsZD1ZYyhhKTtpZihkIT09Yyl7Yz1iLmJbY107dmFyIGU9Yy5pbmRleE9mKGEpOzA8PWUmJmMuc3BsaWNlKGUsMSk7Yz1iLmJbZF18fChiLmJbZF09W10pO2MucHVzaChhKTsxPGMubGVuZ3RoJiYoYi5iW2RdPVpjKGMpKX19QWMoYil9fQp2YXIgamQ9Qih7Z2V0IHByZXZpb3VzRWxlbWVudFNpYmxpbmcoKXt2YXIgYT12KHRoaXMpO2lmKGEmJnZvaWQgMCE9PWEucHJldmlvdXNTaWJsaW5nKXtmb3IoYT10aGlzLl9fc2hhZHlfcHJldmlvdXNTaWJsaW5nO2EmJmEubm9kZVR5cGUhPT1Ob2RlLkVMRU1FTlRfTk9ERTspYT1hLl9fc2hhZHlfcHJldmlvdXNTaWJsaW5nO3JldHVybiBhfXJldHVybiB0aGlzLl9fc2hhZHlfbmF0aXZlX3ByZXZpb3VzRWxlbWVudFNpYmxpbmd9LGdldCBuZXh0RWxlbWVudFNpYmxpbmcoKXt2YXIgYT12KHRoaXMpO2lmKGEmJnZvaWQgMCE9PWEubmV4dFNpYmxpbmcpe2ZvcihhPXRoaXMuX19zaGFkeV9uZXh0U2libGluZzthJiZhLm5vZGVUeXBlIT09Tm9kZS5FTEVNRU5UX05PREU7KWE9YS5fX3NoYWR5X25leHRTaWJsaW5nO3JldHVybiBhfXJldHVybiB0aGlzLl9fc2hhZHlfbmF0aXZlX25leHRFbGVtZW50U2libGluZ30sZ2V0IHNsb3QoKXtyZXR1cm4gdGhpcy5nZXRBdHRyaWJ1dGUoInNsb3QiKX0sCnNldCBzbG90KGEpe3RoaXMuX19zaGFkeV9zZXRBdHRyaWJ1dGUoInNsb3QiLGEpfSxnZXQgc2hhZG93Um9vdCgpe3ZhciBhPXYodGhpcyk7cmV0dXJuIGEmJmEucGF8fG51bGx9LGdldCBjbGFzc05hbWUoKXtyZXR1cm4gdGhpcy5nZXRBdHRyaWJ1dGUoImNsYXNzIil8fCIifSxzZXQgY2xhc3NOYW1lKGEpe3RoaXMuX19zaGFkeV9zZXRBdHRyaWJ1dGUoImNsYXNzIixhKX0sc2V0QXR0cmlidXRlOmZ1bmN0aW9uKGEsYil7aWYodGhpcy5vd25lckRvY3VtZW50IT09aGQpdGhpcy5fX3NoYWR5X25hdGl2ZV9zZXRBdHRyaWJ1dGUoYSxiKTtlbHNle3ZhciBjOyhjPUcoKSkmJiJjbGFzcyI9PT1hPyhjLnNldEVsZW1lbnRDbGFzcyh0aGlzLGIpLGM9ITApOmM9ITE7Y3x8KHRoaXMuX19zaGFkeV9uYXRpdmVfc2V0QXR0cmlidXRlKGEsYiksaWQodGhpcyxhKSl9fSxyZW1vdmVBdHRyaWJ1dGU6ZnVuY3Rpb24oYSl7dGhpcy5fX3NoYWR5X25hdGl2ZV9yZW1vdmVBdHRyaWJ1dGUoYSk7aWQodGhpcywKYSl9LGF0dGFjaFNoYWRvdzpmdW5jdGlvbihhKXtpZighdGhpcyl0aHJvdyBFcnJvcigiTXVzdCBwcm92aWRlIGEgaG9zdC4iKTtpZighYSl0aHJvdyBFcnJvcigiTm90IGVub3VnaCBhcmd1bWVudHMuIik7cmV0dXJuIG5ldyBTYyhPYyx0aGlzLGEpfX0pO3ZhciBrZD1CKHtibHVyOmZ1bmN0aW9uKCl7dmFyIGE9dih0aGlzKTsoYT0oYT1hJiZhLnJvb3QpJiZhLmFjdGl2ZUVsZW1lbnQpP2EuX19zaGFkeV9ibHVyKCk6dGhpcy5fX3NoYWR5X25hdGl2ZV9ibHVyKCl9fSk7Y2MuZm9yRWFjaChmdW5jdGlvbihhKXtrZFthXT17c2V0OmZ1bmN0aW9uKGIpe3ZhciBjPXUodGhpcyksZD1hLnN1YnN0cmluZygyKTtjLk9bYV0mJnRoaXMucmVtb3ZlRXZlbnRMaXN0ZW5lcihkLGMuT1thXSk7dGhpcy5fX3NoYWR5X2FkZEV2ZW50TGlzdGVuZXIoZCxiKTtjLk9bYV09Yn0sZ2V0OmZ1bmN0aW9uKCl7dmFyIGI9dih0aGlzKTtyZXR1cm4gYiYmYi5PW2FdfSxjb25maWd1cmFibGU6ITB9fSk7dmFyIGxkPUIoe2Fzc2lnbmVkTm9kZXM6ZnVuY3Rpb24oYSl7aWYoInNsb3QiPT09dGhpcy5sb2NhbE5hbWUpe3ZhciBiPXRoaXMuX19zaGFkeV9nZXRSb290Tm9kZSgpO2ImJnooYikmJlRjKGIpO3JldHVybihiPXYodGhpcykpPyhhJiZhLmZsYXR0ZW4/Yi5EOmIuYXNzaWduZWROb2Rlcyl8fFtdOltdfX19KTt2YXIgbWQ9d2luZG93LmRvY3VtZW50LG5kPUIoe2ltcG9ydE5vZGU6ZnVuY3Rpb24oYSxiKXtpZihhLm93bmVyRG9jdW1lbnQhPT1tZHx8InRlbXBsYXRlIj09PWEubG9jYWxOYW1lKXJldHVybiB0aGlzLl9fc2hhZHlfbmF0aXZlX2ltcG9ydE5vZGUoYSxiKTt2YXIgYz10aGlzLl9fc2hhZHlfbmF0aXZlX2ltcG9ydE5vZGUoYSwhMSk7aWYoYil7YT1hLl9fc2hhZHlfY2hpbGROb2RlcztiPTA7Zm9yKHZhciBkO2I8YS5sZW5ndGg7YisrKWQ9dGhpcy5fX3NoYWR5X2ltcG9ydE5vZGUoYVtiXSwhMCksYy5fX3NoYWR5X2FwcGVuZENoaWxkKGQpfXJldHVybiBjfX0pO3ZhciBvZD1CKHthZGRFdmVudExpc3RlbmVyOlViLmJpbmQod2luZG93KSxyZW1vdmVFdmVudExpc3RlbmVyOldiLmJpbmQod2luZG93KX0pO3ZhciBwZD17fTtPYmplY3QuZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yKEhUTUxFbGVtZW50LnByb3RvdHlwZSwicGFyZW50RWxlbWVudCIpJiYocGQucGFyZW50RWxlbWVudD1DYy5wYXJlbnRFbGVtZW50KTtPYmplY3QuZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yKEhUTUxFbGVtZW50LnByb3RvdHlwZSwiY29udGFpbnMiKSYmKHBkLmNvbnRhaW5zPUNjLmNvbnRhaW5zKTtPYmplY3QuZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yKEhUTUxFbGVtZW50LnByb3RvdHlwZSwiY2hpbGRyZW4iKSYmKHBkLmNoaWxkcmVuPUZjLmNoaWxkcmVuKTtPYmplY3QuZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yKEhUTUxFbGVtZW50LnByb3RvdHlwZSwiaW5uZXJIVE1MIikmJihwZC5pbm5lckhUTUw9TGMuaW5uZXJIVE1MKTtPYmplY3QuZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yKEhUTUxFbGVtZW50LnByb3RvdHlwZSwiY2xhc3NOYW1lIikmJihwZC5jbGFzc05hbWU9amQuY2xhc3NOYW1lKTsKdmFyIHFkPXtFdmVudFRhcmdldDpbZmRdLE5vZGU6W0NjLHdpbmRvdy5FdmVudFRhcmdldD9udWxsOmZkXSxUZXh0OltnZF0sRWxlbWVudDpbamQsRmMsZ2QsIXcubXx8ImlubmVySFRNTCJpbiBFbGVtZW50LnByb3RvdHlwZT9MYzpudWxsLHdpbmRvdy5IVE1MU2xvdEVsZW1lbnQ/bnVsbDpsZF0sSFRNTEVsZW1lbnQ6W2tkLHBkXSxIVE1MU2xvdEVsZW1lbnQ6W2xkXSxEb2N1bWVudEZyYWdtZW50OltIYyxJY10sRG9jdW1lbnQ6W25kLEhjLEljLEpjXSxXaW5kb3c6W29kXX0scmQ9dy5tP251bGw6WyJpbm5lckhUTUwiLCJ0ZXh0Q29udGVudCJdO2Z1bmN0aW9uIHNkKGEpe3ZhciBiPWE/bnVsbDpyZCxjPXt9LGQ7Zm9yKGQgaW4gcWQpYy5XPXdpbmRvd1tkXSYmd2luZG93W2RdLnByb3RvdHlwZSxxZFtkXS5mb3JFYWNoKGZ1bmN0aW9uKGMpe3JldHVybiBmdW5jdGlvbihkKXtyZXR1cm4gYy5XJiZkJiZBKGMuVyxkLGEsYil9fShjKSksYz17VzpjLld9fTtpZih3LmVhKXt2YXIgU2hhZHlET009e2luVXNlOncuZWEscGF0Y2g6ZnVuY3Rpb24oYSl7RmIoYSk7RWIoYSk7cmV0dXJuIGF9LGlzU2hhZHlSb290OnosZW5xdWV1ZTpZYSxmbHVzaDpaYSxmbHVzaEluaXRpYWw6ZnVuY3Rpb24oYSl7IWEuYmEmJmEuQiYmVGMoYSl9LHNldHRpbmdzOncsZmlsdGVyTXV0YXRpb25zOmRiLG9ic2VydmVDaGlsZHJlbjpiYix1bm9ic2VydmVDaGlsZHJlbjpjYixkZWZlckNvbm5lY3Rpb25DYWxsYmFja3M6dy5kZWZlckNvbm5lY3Rpb25DYWxsYmFja3MscHJlZmVyUGVyZm9ybWFuY2U6dy5wcmVmZXJQZXJmb3JtYW5jZSxoYW5kbGVzRHluYW1pY1Njb3Bpbmc6ITAsd3JhcDp3Lko/ZWQ6ZnVuY3Rpb24oYSl7cmV0dXJuIGF9LFdyYXBwZXI6Y2QsY29tcG9zZWRQYXRoOk1iLG5vUGF0Y2g6dy5KLG5hdGl2ZU1ldGhvZHM6cWIsbmF0aXZlVHJlZTpyYn07d2luZG93LlNoYWR5RE9NPVNoYWR5RE9NO3liKCk7c2QoIl9fc2hhZHlfIik7T2JqZWN0LmRlZmluZVByb3BlcnR5KGRvY3VtZW50LAoiX2FjdGl2ZUVsZW1lbnQiLEpjLmFjdGl2ZUVsZW1lbnQpO0EoV2luZG93LnByb3RvdHlwZSxvZCwiX19zaGFkeV8iKTt3Lkp8fChzZCgpLGJjKCkpO1hiKCk7d2luZG93LkV2ZW50PVpiO3dpbmRvdy5DdXN0b21FdmVudD0kYjt3aW5kb3cuTW91c2VFdmVudD1hYzt3aW5kb3cuU2hhZG93Um9vdD1TY307dmFyIHRkPW5ldyBTZXQoImFubm90YXRpb24teG1sIGNvbG9yLXByb2ZpbGUgZm9udC1mYWNlIGZvbnQtZmFjZS1zcmMgZm9udC1mYWNlLXVyaSBmb250LWZhY2UtZm9ybWF0IGZvbnQtZmFjZS1uYW1lIG1pc3NpbmctZ2x5cGgiLnNwbGl0KCIgIikpO2Z1bmN0aW9uIHVkKGEpe3ZhciBiPXRkLmhhcyhhKTthPS9eW2Etel1bLjAtOV9hLXpdKi1bXC0uMC05X2Etel0qJC8udGVzdChhKTtyZXR1cm4hYiYmYX1mdW5jdGlvbiBIKGEpe3ZhciBiPWEuaXNDb25uZWN0ZWQ7aWYodm9pZCAwIT09YilyZXR1cm4gYjtmb3IoO2EmJiEoYS5fX0NFX2lzSW1wb3J0RG9jdW1lbnR8fGEgaW5zdGFuY2VvZiBEb2N1bWVudCk7KWE9YS5wYXJlbnROb2RlfHwod2luZG93LlNoYWRvd1Jvb3QmJmEgaW5zdGFuY2VvZiBTaGFkb3dSb290P2EuaG9zdDp2b2lkIDApO3JldHVybiEoIWF8fCEoYS5fX0NFX2lzSW1wb3J0RG9jdW1lbnR8fGEgaW5zdGFuY2VvZiBEb2N1bWVudCkpfQpmdW5jdGlvbiB2ZChhLGIpe2Zvcig7YiYmYiE9PWEmJiFiLm5leHRTaWJsaW5nOyliPWIucGFyZW50Tm9kZTtyZXR1cm4gYiYmYiE9PWE/Yi5uZXh0U2libGluZzpudWxsfQpmdW5jdGlvbiB3ZChhLGIsYyl7Yz12b2lkIDA9PT1jP25ldyBTZXQ6Yztmb3IodmFyIGQ9YTtkOyl7aWYoZC5ub2RlVHlwZT09PU5vZGUuRUxFTUVOVF9OT0RFKXt2YXIgZT1kO2IoZSk7dmFyIGY9ZS5sb2NhbE5hbWU7aWYoImxpbmsiPT09ZiYmImltcG9ydCI9PT1lLmdldEF0dHJpYnV0ZSgicmVsIikpe2Q9ZS5pbXBvcnQ7aWYoZCBpbnN0YW5jZW9mIE5vZGUmJiFjLmhhcyhkKSlmb3IoYy5hZGQoZCksZD1kLmZpcnN0Q2hpbGQ7ZDtkPWQubmV4dFNpYmxpbmcpd2QoZCxiLGMpO2Q9dmQoYSxlKTtjb250aW51ZX1lbHNlIGlmKCJ0ZW1wbGF0ZSI9PT1mKXtkPXZkKGEsZSk7Y29udGludWV9aWYoZT1lLl9fQ0Vfc2hhZG93Um9vdClmb3IoZT1lLmZpcnN0Q2hpbGQ7ZTtlPWUubmV4dFNpYmxpbmcpd2QoZSxiLGMpfWQ9ZC5maXJzdENoaWxkP2QuZmlyc3RDaGlsZDp2ZChhLGQpfX1mdW5jdGlvbiBJKGEsYixjKXthW2JdPWN9O2Z1bmN0aW9uIHhkKCl7dGhpcy5hPW5ldyBNYXA7dGhpcy5nPW5ldyBNYXA7dGhpcy5mPVtdO3RoaXMuYz0hMX1mdW5jdGlvbiB5ZChhLGIsYyl7YS5hLnNldChiLGMpO2EuZy5zZXQoYy5jb25zdHJ1Y3RvckZ1bmN0aW9uLGMpfWZ1bmN0aW9uIHpkKGEsYil7YS5jPSEwO2EuZi5wdXNoKGIpfWZ1bmN0aW9uIEFkKGEsYil7YS5jJiZ3ZChiLGZ1bmN0aW9uKGIpe3JldHVybiBhLmIoYil9KX14ZC5wcm90b3R5cGUuYj1mdW5jdGlvbihhKXtpZih0aGlzLmMmJiFhLl9fQ0VfcGF0Y2hlZCl7YS5fX0NFX3BhdGNoZWQ9ITA7Zm9yKHZhciBiPTA7Yjx0aGlzLmYubGVuZ3RoO2IrKyl0aGlzLmZbYl0oYSl9fTtmdW5jdGlvbiBKKGEsYil7dmFyIGM9W107d2QoYixmdW5jdGlvbihhKXtyZXR1cm4gYy5wdXNoKGEpfSk7Zm9yKGI9MDtiPGMubGVuZ3RoO2IrKyl7dmFyIGQ9Y1tiXTsxPT09ZC5fX0NFX3N0YXRlP2EuY29ubmVjdGVkQ2FsbGJhY2soZCk6QmQoYSxkKX19CmZ1bmN0aW9uIEsoYSxiKXt2YXIgYz1bXTt3ZChiLGZ1bmN0aW9uKGEpe3JldHVybiBjLnB1c2goYSl9KTtmb3IoYj0wO2I8Yy5sZW5ndGg7YisrKXt2YXIgZD1jW2JdOzE9PT1kLl9fQ0Vfc3RhdGUmJmEuZGlzY29ubmVjdGVkQ2FsbGJhY2soZCl9fQpmdW5jdGlvbiBMKGEsYixjKXtjPXZvaWQgMD09PWM/e306Yzt2YXIgZD1jLlhhfHxuZXcgU2V0LGU9Yy5ZfHxmdW5jdGlvbihiKXtyZXR1cm4gQmQoYSxiKX0sZj1bXTt3ZChiLGZ1bmN0aW9uKGIpe2lmKCJsaW5rIj09PWIubG9jYWxOYW1lJiYiaW1wb3J0Ij09PWIuZ2V0QXR0cmlidXRlKCJyZWwiKSl7dmFyIGM9Yi5pbXBvcnQ7YyBpbnN0YW5jZW9mIE5vZGUmJihjLl9fQ0VfaXNJbXBvcnREb2N1bWVudD0hMCxjLl9fQ0VfaGFzUmVnaXN0cnk9ITApO2MmJiJjb21wbGV0ZSI9PT1jLnJlYWR5U3RhdGU/Yy5fX0NFX2RvY3VtZW50TG9hZEhhbmRsZWQ9ITA6Yi5hZGRFdmVudExpc3RlbmVyKCJsb2FkIixmdW5jdGlvbigpe3ZhciBjPWIuaW1wb3J0O2lmKCFjLl9fQ0VfZG9jdW1lbnRMb2FkSGFuZGxlZCl7Yy5fX0NFX2RvY3VtZW50TG9hZEhhbmRsZWQ9ITA7dmFyIGY9bmV3IFNldChkKTtmLmRlbGV0ZShjKTtMKGEsYyx7WGE6ZixZOmV9KX19KX1lbHNlIGYucHVzaChiKX0sZCk7aWYoYS5jKWZvcihiPQowO2I8Zi5sZW5ndGg7YisrKWEuYihmW2JdKTtmb3IoYj0wO2I8Zi5sZW5ndGg7YisrKWUoZltiXSl9CmZ1bmN0aW9uIEJkKGEsYil7aWYodm9pZCAwPT09Yi5fX0NFX3N0YXRlKXt2YXIgYz1iLm93bmVyRG9jdW1lbnQ7aWYoYy5kZWZhdWx0Vmlld3x8Yy5fX0NFX2lzSW1wb3J0RG9jdW1lbnQmJmMuX19DRV9oYXNSZWdpc3RyeSlpZihjPWEuYS5nZXQoYi5sb2NhbE5hbWUpKXtjLmNvbnN0cnVjdGlvblN0YWNrLnB1c2goYik7dmFyIGQ9Yy5jb25zdHJ1Y3RvckZ1bmN0aW9uO3RyeXt0cnl7aWYobmV3IGQhPT1iKXRocm93IEVycm9yKCJUaGUgY3VzdG9tIGVsZW1lbnQgY29uc3RydWN0b3IgZGlkIG5vdCBwcm9kdWNlIHRoZSBlbGVtZW50IGJlaW5nIHVwZ3JhZGVkLiIpO31maW5hbGx5e2MuY29uc3RydWN0aW9uU3RhY2sucG9wKCl9fWNhdGNoKGcpe3Rocm93IGIuX19DRV9zdGF0ZT0yLGc7fWIuX19DRV9zdGF0ZT0xO2IuX19DRV9kZWZpbml0aW9uPWM7aWYoYy5hdHRyaWJ1dGVDaGFuZ2VkQ2FsbGJhY2spZm9yKGM9Yy5vYnNlcnZlZEF0dHJpYnV0ZXMsZD0wO2Q8Yy5sZW5ndGg7ZCsrKXt2YXIgZT0KY1tkXSxmPWIuZ2V0QXR0cmlidXRlKGUpO251bGwhPT1mJiZhLmF0dHJpYnV0ZUNoYW5nZWRDYWxsYmFjayhiLGUsbnVsbCxmLG51bGwpfUgoYikmJmEuY29ubmVjdGVkQ2FsbGJhY2soYil9fX14ZC5wcm90b3R5cGUuY29ubmVjdGVkQ2FsbGJhY2s9ZnVuY3Rpb24oYSl7dmFyIGI9YS5fX0NFX2RlZmluaXRpb247Yi5jb25uZWN0ZWRDYWxsYmFjayYmYi5jb25uZWN0ZWRDYWxsYmFjay5jYWxsKGEpfTt4ZC5wcm90b3R5cGUuZGlzY29ubmVjdGVkQ2FsbGJhY2s9ZnVuY3Rpb24oYSl7dmFyIGI9YS5fX0NFX2RlZmluaXRpb247Yi5kaXNjb25uZWN0ZWRDYWxsYmFjayYmYi5kaXNjb25uZWN0ZWRDYWxsYmFjay5jYWxsKGEpfTsKeGQucHJvdG90eXBlLmF0dHJpYnV0ZUNoYW5nZWRDYWxsYmFjaz1mdW5jdGlvbihhLGIsYyxkLGUpe3ZhciBmPWEuX19DRV9kZWZpbml0aW9uO2YuYXR0cmlidXRlQ2hhbmdlZENhbGxiYWNrJiYtMTxmLm9ic2VydmVkQXR0cmlidXRlcy5pbmRleE9mKGIpJiZmLmF0dHJpYnV0ZUNoYW5nZWRDYWxsYmFjay5jYWxsKGEsYixjLGQsZSl9O2Z1bmN0aW9uIENkKGEpe3ZhciBiPWRvY3VtZW50O3RoaXMuYj1hO3RoaXMuYT1iO3RoaXMuRj12b2lkIDA7TCh0aGlzLmIsdGhpcy5hKTsibG9hZGluZyI9PT10aGlzLmEucmVhZHlTdGF0ZSYmKHRoaXMuRj1uZXcgTXV0YXRpb25PYnNlcnZlcih0aGlzLmMuYmluZCh0aGlzKSksdGhpcy5GLm9ic2VydmUodGhpcy5hLHtjaGlsZExpc3Q6ITAsc3VidHJlZTohMH0pKX1mdW5jdGlvbiBEZChhKXthLkYmJmEuRi5kaXNjb25uZWN0KCl9Q2QucHJvdG90eXBlLmM9ZnVuY3Rpb24oYSl7dmFyIGI9dGhpcy5hLnJlYWR5U3RhdGU7ImludGVyYWN0aXZlIiE9PWImJiJjb21wbGV0ZSIhPT1ifHxEZCh0aGlzKTtmb3IoYj0wO2I8YS5sZW5ndGg7YisrKWZvcih2YXIgYz1hW2JdLmFkZGVkTm9kZXMsZD0wO2Q8Yy5sZW5ndGg7ZCsrKUwodGhpcy5iLGNbZF0pfTtmdW5jdGlvbiBFZCgpe3ZhciBhPXRoaXM7dGhpcy5hPXRoaXMuaD12b2lkIDA7dGhpcy5iPW5ldyBQcm9taXNlKGZ1bmN0aW9uKGIpe2EuYT1iO2EuaCYmYihhLmgpfSl9RWQucHJvdG90eXBlLnJlc29sdmU9ZnVuY3Rpb24oYSl7aWYodGhpcy5oKXRocm93IEVycm9yKCJBbHJlYWR5IHJlc29sdmVkLiIpO3RoaXMuaD1hO3RoaXMuYSYmdGhpcy5hKGEpfTtmdW5jdGlvbiBOKGEpe3RoaXMuYz0hMTt0aGlzLmE9YTt0aGlzLmw9bmV3IE1hcDt0aGlzLmY9ZnVuY3Rpb24oYSl7cmV0dXJuIGEoKX07dGhpcy5iPSExO3RoaXMuZz1bXTt0aGlzLmRhPW5ldyBDZChhKX1uPU4ucHJvdG90eXBlOwpuLnNhPWZ1bmN0aW9uKGEsYil7dmFyIGM9dGhpcztpZighKGIgaW5zdGFuY2VvZiBGdW5jdGlvbikpdGhyb3cgbmV3IFR5cGVFcnJvcigiQ3VzdG9tIGVsZW1lbnQgY29uc3RydWN0b3JzIG11c3QgYmUgZnVuY3Rpb25zLiIpO2lmKCF1ZChhKSl0aHJvdyBuZXcgU3ludGF4RXJyb3IoIlRoZSBlbGVtZW50IG5hbWUgJyIrYSsiJyBpcyBub3QgdmFsaWQuIik7aWYodGhpcy5hLmEuZ2V0KGEpKXRocm93IEVycm9yKCJBIGN1c3RvbSBlbGVtZW50IHdpdGggbmFtZSAnIithKyInIGhhcyBhbHJlYWR5IGJlZW4gZGVmaW5lZC4iKTtpZih0aGlzLmMpdGhyb3cgRXJyb3IoIkEgY3VzdG9tIGVsZW1lbnQgaXMgYWxyZWFkeSBiZWluZyBkZWZpbmVkLiIpO3RoaXMuYz0hMDt0cnl7dmFyIGQ9ZnVuY3Rpb24oYSl7dmFyIGI9ZVthXTtpZih2b2lkIDAhPT1iJiYhKGIgaW5zdGFuY2VvZiBGdW5jdGlvbikpdGhyb3cgRXJyb3IoIlRoZSAnIithKyInIGNhbGxiYWNrIG11c3QgYmUgYSBmdW5jdGlvbi4iKTsKcmV0dXJuIGJ9LGU9Yi5wcm90b3R5cGU7aWYoIShlIGluc3RhbmNlb2YgT2JqZWN0KSl0aHJvdyBuZXcgVHlwZUVycm9yKCJUaGUgY3VzdG9tIGVsZW1lbnQgY29uc3RydWN0b3IncyBwcm90b3R5cGUgaXMgbm90IGFuIG9iamVjdC4iKTt2YXIgZj1kKCJjb25uZWN0ZWRDYWxsYmFjayIpO3ZhciBnPWQoImRpc2Nvbm5lY3RlZENhbGxiYWNrIik7dmFyIGg9ZCgiYWRvcHRlZENhbGxiYWNrIik7dmFyIGs9ZCgiYXR0cmlidXRlQ2hhbmdlZENhbGxiYWNrIik7dmFyIGw9Yi5vYnNlcnZlZEF0dHJpYnV0ZXN8fFtdfWNhdGNoKG0pe3JldHVybn1maW5hbGx5e3RoaXMuYz0hMX1iPXtsb2NhbE5hbWU6YSxjb25zdHJ1Y3RvckZ1bmN0aW9uOmIsY29ubmVjdGVkQ2FsbGJhY2s6ZixkaXNjb25uZWN0ZWRDYWxsYmFjazpnLGFkb3B0ZWRDYWxsYmFjazpoLGF0dHJpYnV0ZUNoYW5nZWRDYWxsYmFjazprLG9ic2VydmVkQXR0cmlidXRlczpsLGNvbnN0cnVjdGlvblN0YWNrOltdfTt5ZCh0aGlzLmEsCmEsYik7dGhpcy5nLnB1c2goYik7dGhpcy5ifHwodGhpcy5iPSEwLHRoaXMuZihmdW5jdGlvbigpe3JldHVybiBGZChjKX0pKX07bi5ZPWZ1bmN0aW9uKGEpe0wodGhpcy5hLGEpfTsKZnVuY3Rpb24gRmQoYSl7aWYoITEhPT1hLmIpe2EuYj0hMTtmb3IodmFyIGI9YS5nLGM9W10sZD1uZXcgTWFwLGU9MDtlPGIubGVuZ3RoO2UrKylkLnNldChiW2VdLmxvY2FsTmFtZSxbXSk7TChhLmEsZG9jdW1lbnQse1k6ZnVuY3Rpb24oYil7aWYodm9pZCAwPT09Yi5fX0NFX3N0YXRlKXt2YXIgZT1iLmxvY2FsTmFtZSxmPWQuZ2V0KGUpO2Y/Zi5wdXNoKGIpOmEuYS5hLmdldChlKSYmYy5wdXNoKGIpfX19KTtmb3IoZT0wO2U8Yy5sZW5ndGg7ZSsrKUJkKGEuYSxjW2VdKTtmb3IoOzA8Yi5sZW5ndGg7KXt2YXIgZj1iLnNoaWZ0KCk7ZT1mLmxvY2FsTmFtZTtmPWQuZ2V0KGYubG9jYWxOYW1lKTtmb3IodmFyIGc9MDtnPGYubGVuZ3RoO2crKylCZChhLmEsZltnXSk7KGU9YS5sLmdldChlKSkmJmUucmVzb2x2ZSh2b2lkIDApfX19bi5nZXQ9ZnVuY3Rpb24oYSl7aWYoYT10aGlzLmEuYS5nZXQoYSkpcmV0dXJuIGEuY29uc3RydWN0b3JGdW5jdGlvbn07Cm4udGE9ZnVuY3Rpb24oYSl7aWYoIXVkKGEpKXJldHVybiBQcm9taXNlLnJlamVjdChuZXcgU3ludGF4RXJyb3IoIiciK2ErIicgaXMgbm90IGEgdmFsaWQgY3VzdG9tIGVsZW1lbnQgbmFtZS4iKSk7dmFyIGI9dGhpcy5sLmdldChhKTtpZihiKXJldHVybiBiLmI7Yj1uZXcgRWQ7dGhpcy5sLnNldChhLGIpO3RoaXMuYS5hLmdldChhKSYmIXRoaXMuZy5zb21lKGZ1bmN0aW9uKGIpe3JldHVybiBiLmxvY2FsTmFtZT09PWF9KSYmYi5yZXNvbHZlKHZvaWQgMCk7cmV0dXJuIGIuYn07bi5QYT1mdW5jdGlvbihhKXtEZCh0aGlzLmRhKTt2YXIgYj10aGlzLmY7dGhpcy5mPWZ1bmN0aW9uKGMpe3JldHVybiBhKGZ1bmN0aW9uKCl7cmV0dXJuIGIoYyl9KX19O3dpbmRvdy5DdXN0b21FbGVtZW50UmVnaXN0cnk9TjtOLnByb3RvdHlwZS5kZWZpbmU9Ti5wcm90b3R5cGUuc2E7Ti5wcm90b3R5cGUudXBncmFkZT1OLnByb3RvdHlwZS5ZO04ucHJvdG90eXBlLmdldD1OLnByb3RvdHlwZS5nZXQ7Ck4ucHJvdG90eXBlLndoZW5EZWZpbmVkPU4ucHJvdG90eXBlLnRhO04ucHJvdG90eXBlLnBvbHlmaWxsV3JhcEZsdXNoQ2FsbGJhY2s9Ti5wcm90b3R5cGUuUGE7dmFyIEdkPXdpbmRvdy5Eb2N1bWVudC5wcm90b3R5cGUuY3JlYXRlRWxlbWVudCxIZD13aW5kb3cuRG9jdW1lbnQucHJvdG90eXBlLmNyZWF0ZUVsZW1lbnROUyxJZD13aW5kb3cuRG9jdW1lbnQucHJvdG90eXBlLmltcG9ydE5vZGUsSmQ9d2luZG93LkRvY3VtZW50LnByb3RvdHlwZS5wcmVwZW5kLEtkPXdpbmRvdy5Eb2N1bWVudC5wcm90b3R5cGUuYXBwZW5kLExkPXdpbmRvdy5Eb2N1bWVudEZyYWdtZW50LnByb3RvdHlwZS5wcmVwZW5kLE1kPXdpbmRvdy5Eb2N1bWVudEZyYWdtZW50LnByb3RvdHlwZS5hcHBlbmQsTmQ9d2luZG93Lk5vZGUucHJvdG90eXBlLmNsb25lTm9kZSxPZD13aW5kb3cuTm9kZS5wcm90b3R5cGUuYXBwZW5kQ2hpbGQsUGQ9d2luZG93Lk5vZGUucHJvdG90eXBlLmluc2VydEJlZm9yZSxRZD13aW5kb3cuTm9kZS5wcm90b3R5cGUucmVtb3ZlQ2hpbGQsUmQ9d2luZG93Lk5vZGUucHJvdG90eXBlLnJlcGxhY2VDaGlsZCxTZD1PYmplY3QuZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yKHdpbmRvdy5Ob2RlLnByb3RvdHlwZSwKInRleHRDb250ZW50IiksVGQ9d2luZG93LkVsZW1lbnQucHJvdG90eXBlLmF0dGFjaFNoYWRvdyxVZD1PYmplY3QuZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yKHdpbmRvdy5FbGVtZW50LnByb3RvdHlwZSwiaW5uZXJIVE1MIiksVmQ9d2luZG93LkVsZW1lbnQucHJvdG90eXBlLmdldEF0dHJpYnV0ZSxXZD13aW5kb3cuRWxlbWVudC5wcm90b3R5cGUuc2V0QXR0cmlidXRlLFhkPXdpbmRvdy5FbGVtZW50LnByb3RvdHlwZS5yZW1vdmVBdHRyaWJ1dGUsWWQ9d2luZG93LkVsZW1lbnQucHJvdG90eXBlLmdldEF0dHJpYnV0ZU5TLFpkPXdpbmRvdy5FbGVtZW50LnByb3RvdHlwZS5zZXRBdHRyaWJ1dGVOUywkZD13aW5kb3cuRWxlbWVudC5wcm90b3R5cGUucmVtb3ZlQXR0cmlidXRlTlMsYWU9d2luZG93LkVsZW1lbnQucHJvdG90eXBlLmluc2VydEFkamFjZW50RWxlbWVudCxiZT13aW5kb3cuRWxlbWVudC5wcm90b3R5cGUuaW5zZXJ0QWRqYWNlbnRIVE1MLGNlPXdpbmRvdy5FbGVtZW50LnByb3RvdHlwZS5wcmVwZW5kLApkZT13aW5kb3cuRWxlbWVudC5wcm90b3R5cGUuYXBwZW5kLGVlPXdpbmRvdy5FbGVtZW50LnByb3RvdHlwZS5iZWZvcmUsZmU9d2luZG93LkVsZW1lbnQucHJvdG90eXBlLmFmdGVyLGdlPXdpbmRvdy5FbGVtZW50LnByb3RvdHlwZS5yZXBsYWNlV2l0aCxoZT13aW5kb3cuRWxlbWVudC5wcm90b3R5cGUucmVtb3ZlLGllPXdpbmRvdy5IVE1MRWxlbWVudCxqZT1PYmplY3QuZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yKHdpbmRvdy5IVE1MRWxlbWVudC5wcm90b3R5cGUsImlubmVySFRNTCIpLGtlPXdpbmRvdy5IVE1MRWxlbWVudC5wcm90b3R5cGUuaW5zZXJ0QWRqYWNlbnRFbGVtZW50LGxlPXdpbmRvdy5IVE1MRWxlbWVudC5wcm90b3R5cGUuaW5zZXJ0QWRqYWNlbnRIVE1MO3ZhciBtZT1uZXcgZnVuY3Rpb24oKXt9O2Z1bmN0aW9uIG5lKCl7dmFyIGE9b2U7d2luZG93LkhUTUxFbGVtZW50PWZ1bmN0aW9uKCl7ZnVuY3Rpb24gYigpe3ZhciBiPXRoaXMuY29uc3RydWN0b3IsZD1hLmcuZ2V0KGIpO2lmKCFkKXRocm93IEVycm9yKCJUaGUgY3VzdG9tIGVsZW1lbnQgYmVpbmcgY29uc3RydWN0ZWQgd2FzIG5vdCByZWdpc3RlcmVkIHdpdGggYGN1c3RvbUVsZW1lbnRzYC4iKTt2YXIgZT1kLmNvbnN0cnVjdGlvblN0YWNrO2lmKDA9PT1lLmxlbmd0aClyZXR1cm4gZT1HZC5jYWxsKGRvY3VtZW50LGQubG9jYWxOYW1lKSxPYmplY3Quc2V0UHJvdG90eXBlT2YoZSxiLnByb3RvdHlwZSksZS5fX0NFX3N0YXRlPTEsZS5fX0NFX2RlZmluaXRpb249ZCxhLmIoZSksZTtkPWUubGVuZ3RoLTE7dmFyIGY9ZVtkXTtpZihmPT09bWUpdGhyb3cgRXJyb3IoIlRoZSBIVE1MRWxlbWVudCBjb25zdHJ1Y3RvciB3YXMgZWl0aGVyIGNhbGxlZCByZWVudHJhbnRseSBmb3IgdGhpcyBjb25zdHJ1Y3RvciBvciBjYWxsZWQgbXVsdGlwbGUgdGltZXMuIik7CmVbZF09bWU7T2JqZWN0LnNldFByb3RvdHlwZU9mKGYsYi5wcm90b3R5cGUpO2EuYihmKTtyZXR1cm4gZn1iLnByb3RvdHlwZT1pZS5wcm90b3R5cGU7T2JqZWN0LmRlZmluZVByb3BlcnR5KGIucHJvdG90eXBlLCJjb25zdHJ1Y3RvciIse3dyaXRhYmxlOiEwLGNvbmZpZ3VyYWJsZTohMCxlbnVtZXJhYmxlOiExLHZhbHVlOmJ9KTtyZXR1cm4gYn0oKX07ZnVuY3Rpb24gcGUoYSxiLGMpe2Z1bmN0aW9uIGQoYil7cmV0dXJuIGZ1bmN0aW9uKGMpe2Zvcih2YXIgZD1bXSxlPTA7ZTxhcmd1bWVudHMubGVuZ3RoOysrZSlkW2VdPWFyZ3VtZW50c1tlXTtlPVtdO2Zvcih2YXIgZj1bXSxsPTA7bDxkLmxlbmd0aDtsKyspe3ZhciBtPWRbbF07bSBpbnN0YW5jZW9mIEVsZW1lbnQmJkgobSkmJmYucHVzaChtKTtpZihtIGluc3RhbmNlb2YgRG9jdW1lbnRGcmFnbWVudClmb3IobT1tLmZpcnN0Q2hpbGQ7bTttPW0ubmV4dFNpYmxpbmcpZS5wdXNoKG0pO2Vsc2UgZS5wdXNoKG0pfWIuYXBwbHkodGhpcyxkKTtmb3IoZD0wO2Q8Zi5sZW5ndGg7ZCsrKUsoYSxmW2RdKTtpZihIKHRoaXMpKWZvcihkPTA7ZDxlLmxlbmd0aDtkKyspZj1lW2RdLGYgaW5zdGFuY2VvZiBFbGVtZW50JiZKKGEsZil9fXZvaWQgMCE9PWMuViYmKGIucHJlcGVuZD1kKGMuVikpO3ZvaWQgMCE9PWMuYXBwZW5kJiYoYi5hcHBlbmQ9ZChjLmFwcGVuZCkpfTtmdW5jdGlvbiBxZSgpe3ZhciBhPW9lO0koRG9jdW1lbnQucHJvdG90eXBlLCJjcmVhdGVFbGVtZW50IixmdW5jdGlvbihiKXtpZih0aGlzLl9fQ0VfaGFzUmVnaXN0cnkpe3ZhciBjPWEuYS5nZXQoYik7aWYoYylyZXR1cm4gbmV3IGMuY29uc3RydWN0b3JGdW5jdGlvbn1iPUdkLmNhbGwodGhpcyxiKTthLmIoYik7cmV0dXJuIGJ9KTtJKERvY3VtZW50LnByb3RvdHlwZSwiaW1wb3J0Tm9kZSIsZnVuY3Rpb24oYixjKXtiPUlkLmNhbGwodGhpcyxiLCEhYyk7dGhpcy5fX0NFX2hhc1JlZ2lzdHJ5P0woYSxiKTpBZChhLGIpO3JldHVybiBifSk7SShEb2N1bWVudC5wcm90b3R5cGUsImNyZWF0ZUVsZW1lbnROUyIsZnVuY3Rpb24oYixjKXtpZih0aGlzLl9fQ0VfaGFzUmVnaXN0cnkmJihudWxsPT09Ynx8Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGh0bWwiPT09Yikpe3ZhciBkPWEuYS5nZXQoYyk7aWYoZClyZXR1cm4gbmV3IGQuY29uc3RydWN0b3JGdW5jdGlvbn1iPUhkLmNhbGwodGhpcywKYixjKTthLmIoYik7cmV0dXJuIGJ9KTtwZShhLERvY3VtZW50LnByb3RvdHlwZSx7VjpKZCxhcHBlbmQ6S2R9KX07ZnVuY3Rpb24gcmUoKXtmdW5jdGlvbiBhKGEsZCl7T2JqZWN0LmRlZmluZVByb3BlcnR5KGEsInRleHRDb250ZW50Iix7ZW51bWVyYWJsZTpkLmVudW1lcmFibGUsY29uZmlndXJhYmxlOiEwLGdldDpkLmdldCxzZXQ6ZnVuY3Rpb24oYSl7aWYodGhpcy5ub2RlVHlwZT09PU5vZGUuVEVYVF9OT0RFKWQuc2V0LmNhbGwodGhpcyxhKTtlbHNle3ZhciBjPXZvaWQgMDtpZih0aGlzLmZpcnN0Q2hpbGQpe3ZhciBlPXRoaXMuY2hpbGROb2RlcyxoPWUubGVuZ3RoO2lmKDA8aCYmSCh0aGlzKSl7Yz1BcnJheShoKTtmb3IodmFyIGs9MDtrPGg7aysrKWNba109ZVtrXX19ZC5zZXQuY2FsbCh0aGlzLGEpO2lmKGMpZm9yKGE9MDthPGMubGVuZ3RoO2ErKylLKGIsY1thXSl9fX0pfXZhciBiPW9lO0koTm9kZS5wcm90b3R5cGUsImluc2VydEJlZm9yZSIsZnVuY3Rpb24oYSxkKXtpZihhIGluc3RhbmNlb2YgRG9jdW1lbnRGcmFnbWVudCl7dmFyIGM9QXJyYXkucHJvdG90eXBlLnNsaWNlLmFwcGx5KGEuY2hpbGROb2Rlcyk7CmE9UGQuY2FsbCh0aGlzLGEsZCk7aWYoSCh0aGlzKSlmb3IoZD0wO2Q8Yy5sZW5ndGg7ZCsrKUooYixjW2RdKTtyZXR1cm4gYX1jPUgoYSk7ZD1QZC5jYWxsKHRoaXMsYSxkKTtjJiZLKGIsYSk7SCh0aGlzKSYmSihiLGEpO3JldHVybiBkfSk7SShOb2RlLnByb3RvdHlwZSwiYXBwZW5kQ2hpbGQiLGZ1bmN0aW9uKGEpe2lmKGEgaW5zdGFuY2VvZiBEb2N1bWVudEZyYWdtZW50KXt2YXIgYz1BcnJheS5wcm90b3R5cGUuc2xpY2UuYXBwbHkoYS5jaGlsZE5vZGVzKTthPU9kLmNhbGwodGhpcyxhKTtpZihIKHRoaXMpKWZvcih2YXIgZT0wO2U8Yy5sZW5ndGg7ZSsrKUooYixjW2VdKTtyZXR1cm4gYX1jPUgoYSk7ZT1PZC5jYWxsKHRoaXMsYSk7YyYmSyhiLGEpO0godGhpcykmJkooYixhKTtyZXR1cm4gZX0pO0koTm9kZS5wcm90b3R5cGUsImNsb25lTm9kZSIsZnVuY3Rpb24oYSl7YT1OZC5jYWxsKHRoaXMsISFhKTt0aGlzLm93bmVyRG9jdW1lbnQuX19DRV9oYXNSZWdpc3RyeT9MKGIsYSk6CkFkKGIsYSk7cmV0dXJuIGF9KTtJKE5vZGUucHJvdG90eXBlLCJyZW1vdmVDaGlsZCIsZnVuY3Rpb24oYSl7dmFyIGM9SChhKSxlPVFkLmNhbGwodGhpcyxhKTtjJiZLKGIsYSk7cmV0dXJuIGV9KTtJKE5vZGUucHJvdG90eXBlLCJyZXBsYWNlQ2hpbGQiLGZ1bmN0aW9uKGEsZCl7aWYoYSBpbnN0YW5jZW9mIERvY3VtZW50RnJhZ21lbnQpe3ZhciBjPUFycmF5LnByb3RvdHlwZS5zbGljZS5hcHBseShhLmNoaWxkTm9kZXMpO2E9UmQuY2FsbCh0aGlzLGEsZCk7aWYoSCh0aGlzKSlmb3IoSyhiLGQpLGQ9MDtkPGMubGVuZ3RoO2QrKylKKGIsY1tkXSk7cmV0dXJuIGF9Yz1IKGEpO3ZhciBmPVJkLmNhbGwodGhpcyxhLGQpLGc9SCh0aGlzKTtnJiZLKGIsZCk7YyYmSyhiLGEpO2cmJkooYixhKTtyZXR1cm4gZn0pO1NkJiZTZC5nZXQ/YShOb2RlLnByb3RvdHlwZSxTZCk6emQoYixmdW5jdGlvbihiKXthKGIse2VudW1lcmFibGU6ITAsY29uZmlndXJhYmxlOiEwLGdldDpmdW5jdGlvbigpe2Zvcih2YXIgYT0KW10sYj0wO2I8dGhpcy5jaGlsZE5vZGVzLmxlbmd0aDtiKyspYS5wdXNoKHRoaXMuY2hpbGROb2Rlc1tiXS50ZXh0Q29udGVudCk7cmV0dXJuIGEuam9pbigiIil9LHNldDpmdW5jdGlvbihhKXtmb3IoO3RoaXMuZmlyc3RDaGlsZDspUWQuY2FsbCh0aGlzLHRoaXMuZmlyc3RDaGlsZCk7T2QuY2FsbCh0aGlzLGRvY3VtZW50LmNyZWF0ZVRleHROb2RlKGEpKX19KX0pfTtmdW5jdGlvbiBzZShhKXtmdW5jdGlvbiBiKGIpe3JldHVybiBmdW5jdGlvbihjKXtmb3IodmFyIGQ9W10sZT0wO2U8YXJndW1lbnRzLmxlbmd0aDsrK2UpZFtlXT1hcmd1bWVudHNbZV07ZT1bXTtmb3IodmFyIGg9W10saz0wO2s8ZC5sZW5ndGg7aysrKXt2YXIgbD1kW2tdO2wgaW5zdGFuY2VvZiBFbGVtZW50JiZIKGwpJiZoLnB1c2gobCk7aWYobCBpbnN0YW5jZW9mIERvY3VtZW50RnJhZ21lbnQpZm9yKGw9bC5maXJzdENoaWxkO2w7bD1sLm5leHRTaWJsaW5nKWUucHVzaChsKTtlbHNlIGUucHVzaChsKX1iLmFwcGx5KHRoaXMsZCk7Zm9yKGQ9MDtkPGgubGVuZ3RoO2QrKylLKGEsaFtkXSk7aWYoSCh0aGlzKSlmb3IoZD0wO2Q8ZS5sZW5ndGg7ZCsrKWg9ZVtkXSxoIGluc3RhbmNlb2YgRWxlbWVudCYmSihhLGgpfX12YXIgYz1FbGVtZW50LnByb3RvdHlwZTt2b2lkIDAhPT1lZSYmKGMuYmVmb3JlPWIoZWUpKTt2b2lkIDAhPT1lZSYmKGMuYWZ0ZXI9YihmZSkpO3ZvaWQgMCE9PWdlJiYKSShjLCJyZXBsYWNlV2l0aCIsZnVuY3Rpb24oYil7Zm9yKHZhciBjPVtdLGQ9MDtkPGFyZ3VtZW50cy5sZW5ndGg7KytkKWNbZF09YXJndW1lbnRzW2RdO2Q9W107Zm9yKHZhciBnPVtdLGg9MDtoPGMubGVuZ3RoO2grKyl7dmFyIGs9Y1toXTtrIGluc3RhbmNlb2YgRWxlbWVudCYmSChrKSYmZy5wdXNoKGspO2lmKGsgaW5zdGFuY2VvZiBEb2N1bWVudEZyYWdtZW50KWZvcihrPWsuZmlyc3RDaGlsZDtrO2s9ay5uZXh0U2libGluZylkLnB1c2goayk7ZWxzZSBkLnB1c2goayl9aD1IKHRoaXMpO2dlLmFwcGx5KHRoaXMsYyk7Zm9yKGM9MDtjPGcubGVuZ3RoO2MrKylLKGEsZ1tjXSk7aWYoaClmb3IoSyhhLHRoaXMpLGM9MDtjPGQubGVuZ3RoO2MrKylnPWRbY10sZyBpbnN0YW5jZW9mIEVsZW1lbnQmJkooYSxnKX0pO3ZvaWQgMCE9PWhlJiZJKGMsInJlbW92ZSIsZnVuY3Rpb24oKXt2YXIgYj1IKHRoaXMpO2hlLmNhbGwodGhpcyk7YiYmSyhhLHRoaXMpfSl9O2Z1bmN0aW9uIHRlKCl7ZnVuY3Rpb24gYShhLGIpe09iamVjdC5kZWZpbmVQcm9wZXJ0eShhLCJpbm5lckhUTUwiLHtlbnVtZXJhYmxlOmIuZW51bWVyYWJsZSxjb25maWd1cmFibGU6ITAsZ2V0OmIuZ2V0LHNldDpmdW5jdGlvbihhKXt2YXIgYz10aGlzLGU9dm9pZCAwO0godGhpcykmJihlPVtdLHdkKHRoaXMsZnVuY3Rpb24oYSl7YSE9PWMmJmUucHVzaChhKX0pKTtiLnNldC5jYWxsKHRoaXMsYSk7aWYoZSlmb3IodmFyIGY9MDtmPGUubGVuZ3RoO2YrKyl7dmFyIGc9ZVtmXTsxPT09Zy5fX0NFX3N0YXRlJiZkLmRpc2Nvbm5lY3RlZENhbGxiYWNrKGcpfXRoaXMub3duZXJEb2N1bWVudC5fX0NFX2hhc1JlZ2lzdHJ5P0woZCx0aGlzKTpBZChkLHRoaXMpO3JldHVybiBhfX0pfWZ1bmN0aW9uIGIoYSxiKXtJKGEsImluc2VydEFkamFjZW50RWxlbWVudCIsZnVuY3Rpb24oYSxjKXt2YXIgZT1IKGMpO2E9Yi5jYWxsKHRoaXMsYSxjKTtlJiZLKGQsYyk7SChhKSYmSihkLGMpO3JldHVybiBhfSl9CmZ1bmN0aW9uIGMoYSxiKXtmdW5jdGlvbiBjKGEsYil7Zm9yKHZhciBjPVtdO2EhPT1iO2E9YS5uZXh0U2libGluZyljLnB1c2goYSk7Zm9yKGI9MDtiPGMubGVuZ3RoO2IrKylMKGQsY1tiXSl9SShhLCJpbnNlcnRBZGphY2VudEhUTUwiLGZ1bmN0aW9uKGEsZCl7YT1hLnRvTG93ZXJDYXNlKCk7aWYoImJlZm9yZWJlZ2luIj09PWEpe3ZhciBlPXRoaXMucHJldmlvdXNTaWJsaW5nO2IuY2FsbCh0aGlzLGEsZCk7YyhlfHx0aGlzLnBhcmVudE5vZGUuZmlyc3RDaGlsZCx0aGlzKX1lbHNlIGlmKCJhZnRlcmJlZ2luIj09PWEpZT10aGlzLmZpcnN0Q2hpbGQsYi5jYWxsKHRoaXMsYSxkKSxjKHRoaXMuZmlyc3RDaGlsZCxlKTtlbHNlIGlmKCJiZWZvcmVlbmQiPT09YSllPXRoaXMubGFzdENoaWxkLGIuY2FsbCh0aGlzLGEsZCksYyhlfHx0aGlzLmZpcnN0Q2hpbGQsbnVsbCk7ZWxzZSBpZigiYWZ0ZXJlbmQiPT09YSllPXRoaXMubmV4dFNpYmxpbmcsYi5jYWxsKHRoaXMsYSxkKSxjKHRoaXMubmV4dFNpYmxpbmcsCmUpO2Vsc2UgdGhyb3cgbmV3IFN5bnRheEVycm9yKCJUaGUgdmFsdWUgcHJvdmlkZWQgKCIrU3RyaW5nKGEpKyIpIGlzIG5vdCBvbmUgb2YgJ2JlZm9yZWJlZ2luJywgJ2FmdGVyYmVnaW4nLCAnYmVmb3JlZW5kJywgb3IgJ2FmdGVyZW5kJy4iKTt9KX12YXIgZD1vZTtUZCYmSShFbGVtZW50LnByb3RvdHlwZSwiYXR0YWNoU2hhZG93IixmdW5jdGlvbihhKXtyZXR1cm4gdGhpcy5fX0NFX3NoYWRvd1Jvb3Q9YT1UZC5jYWxsKHRoaXMsYSl9KTtVZCYmVWQuZ2V0P2EoRWxlbWVudC5wcm90b3R5cGUsVWQpOmplJiZqZS5nZXQ/YShIVE1MRWxlbWVudC5wcm90b3R5cGUsamUpOnpkKGQsZnVuY3Rpb24oYil7YShiLHtlbnVtZXJhYmxlOiEwLGNvbmZpZ3VyYWJsZTohMCxnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gTmQuY2FsbCh0aGlzLCEwKS5pbm5lckhUTUx9LHNldDpmdW5jdGlvbihhKXt2YXIgYj0idGVtcGxhdGUiPT09dGhpcy5sb2NhbE5hbWUsYz1iP3RoaXMuY29udGVudDp0aGlzLGQ9SGQuY2FsbChkb2N1bWVudCwKdGhpcy5uYW1lc3BhY2VVUkksdGhpcy5sb2NhbE5hbWUpO2ZvcihkLmlubmVySFRNTD1hOzA8Yy5jaGlsZE5vZGVzLmxlbmd0aDspUWQuY2FsbChjLGMuY2hpbGROb2Rlc1swXSk7Zm9yKGE9Yj9kLmNvbnRlbnQ6ZDswPGEuY2hpbGROb2Rlcy5sZW5ndGg7KU9kLmNhbGwoYyxhLmNoaWxkTm9kZXNbMF0pfX0pfSk7SShFbGVtZW50LnByb3RvdHlwZSwic2V0QXR0cmlidXRlIixmdW5jdGlvbihhLGIpe2lmKDEhPT10aGlzLl9fQ0Vfc3RhdGUpcmV0dXJuIFdkLmNhbGwodGhpcyxhLGIpO3ZhciBjPVZkLmNhbGwodGhpcyxhKTtXZC5jYWxsKHRoaXMsYSxiKTtiPVZkLmNhbGwodGhpcyxhKTtkLmF0dHJpYnV0ZUNoYW5nZWRDYWxsYmFjayh0aGlzLGEsYyxiLG51bGwpfSk7SShFbGVtZW50LnByb3RvdHlwZSwic2V0QXR0cmlidXRlTlMiLGZ1bmN0aW9uKGEsYixjKXtpZigxIT09dGhpcy5fX0NFX3N0YXRlKXJldHVybiBaZC5jYWxsKHRoaXMsYSxiLGMpO3ZhciBlPVlkLmNhbGwodGhpcyxhLApiKTtaZC5jYWxsKHRoaXMsYSxiLGMpO2M9WWQuY2FsbCh0aGlzLGEsYik7ZC5hdHRyaWJ1dGVDaGFuZ2VkQ2FsbGJhY2sodGhpcyxiLGUsYyxhKX0pO0koRWxlbWVudC5wcm90b3R5cGUsInJlbW92ZUF0dHJpYnV0ZSIsZnVuY3Rpb24oYSl7aWYoMSE9PXRoaXMuX19DRV9zdGF0ZSlyZXR1cm4gWGQuY2FsbCh0aGlzLGEpO3ZhciBiPVZkLmNhbGwodGhpcyxhKTtYZC5jYWxsKHRoaXMsYSk7bnVsbCE9PWImJmQuYXR0cmlidXRlQ2hhbmdlZENhbGxiYWNrKHRoaXMsYSxiLG51bGwsbnVsbCl9KTtJKEVsZW1lbnQucHJvdG90eXBlLCJyZW1vdmVBdHRyaWJ1dGVOUyIsZnVuY3Rpb24oYSxiKXtpZigxIT09dGhpcy5fX0NFX3N0YXRlKXJldHVybiAkZC5jYWxsKHRoaXMsYSxiKTt2YXIgYz1ZZC5jYWxsKHRoaXMsYSxiKTskZC5jYWxsKHRoaXMsYSxiKTt2YXIgZT1ZZC5jYWxsKHRoaXMsYSxiKTtjIT09ZSYmZC5hdHRyaWJ1dGVDaGFuZ2VkQ2FsbGJhY2sodGhpcyxiLGMsZSxhKX0pO2tlP2IoSFRNTEVsZW1lbnQucHJvdG90eXBlLAprZSk6YWU/YihFbGVtZW50LnByb3RvdHlwZSxhZSk6Y29uc29sZS53YXJuKCJDdXN0b20gRWxlbWVudHM6IGBFbGVtZW50I2luc2VydEFkamFjZW50RWxlbWVudGAgd2FzIG5vdCBwYXRjaGVkLiIpO2xlP2MoSFRNTEVsZW1lbnQucHJvdG90eXBlLGxlKTpiZT9jKEVsZW1lbnQucHJvdG90eXBlLGJlKTpjb25zb2xlLndhcm4oIkN1c3RvbSBFbGVtZW50czogYEVsZW1lbnQjaW5zZXJ0QWRqYWNlbnRIVE1MYCB3YXMgbm90IHBhdGNoZWQuIik7cGUoZCxFbGVtZW50LnByb3RvdHlwZSx7VjpjZSxhcHBlbmQ6ZGV9KTtzZShkKX07dmFyIHVlPXdpbmRvdy5jdXN0b21FbGVtZW50cztpZighdWV8fHVlLmZvcmNlUG9seWZpbGx8fCJmdW5jdGlvbiIhPXR5cGVvZiB1ZS5kZWZpbmV8fCJmdW5jdGlvbiIhPXR5cGVvZiB1ZS5nZXQpe3ZhciBvZT1uZXcgeGQ7bmUoKTtxZSgpO3BlKG9lLERvY3VtZW50RnJhZ21lbnQucHJvdG90eXBlLHtWOkxkLGFwcGVuZDpNZH0pO3JlKCk7dGUoKTtkb2N1bWVudC5fX0NFX2hhc1JlZ2lzdHJ5PSEwO3ZhciBjdXN0b21FbGVtZW50cz1uZXcgTihvZSk7T2JqZWN0LmRlZmluZVByb3BlcnR5KHdpbmRvdywiY3VzdG9tRWxlbWVudHMiLHtjb25maWd1cmFibGU6ITAsZW51bWVyYWJsZTohMCx2YWx1ZTpjdXN0b21FbGVtZW50c30pfTtmdW5jdGlvbiB2ZSgpe3RoaXMuZW5kPXRoaXMuc3RhcnQ9MDt0aGlzLnJ1bGVzPXRoaXMucGFyZW50PXRoaXMucHJldmlvdXM9bnVsbDt0aGlzLmNzc1RleHQ9dGhpcy5wYXJzZWRDc3NUZXh0PSIiO3RoaXMuYXRSdWxlPSExO3RoaXMudHlwZT0wO3RoaXMucGFyc2VkU2VsZWN0b3I9dGhpcy5zZWxlY3Rvcj10aGlzLmtleWZyYW1lc05hbWU9IiJ9CmZ1bmN0aW9uIHdlKGEpe2E9YS5yZXBsYWNlKHhlLCIiKS5yZXBsYWNlKHllLCIiKTt2YXIgYj16ZSxjPWEsZD1uZXcgdmU7ZC5zdGFydD0wO2QuZW5kPWMubGVuZ3RoO2Zvcih2YXIgZT1kLGY9MCxnPWMubGVuZ3RoO2Y8ZztmKyspaWYoInsiPT09Y1tmXSl7ZS5ydWxlc3x8KGUucnVsZXM9W10pO3ZhciBoPWUsaz1oLnJ1bGVzW2gucnVsZXMubGVuZ3RoLTFdfHxudWxsO2U9bmV3IHZlO2Uuc3RhcnQ9ZisxO2UucGFyZW50PWg7ZS5wcmV2aW91cz1rO2gucnVsZXMucHVzaChlKX1lbHNlIn0iPT09Y1tmXSYmKGUuZW5kPWYrMSxlPWUucGFyZW50fHxkKTtyZXR1cm4gYihkLGEpfQpmdW5jdGlvbiB6ZShhLGIpe3ZhciBjPWIuc3Vic3RyaW5nKGEuc3RhcnQsYS5lbmQtMSk7YS5wYXJzZWRDc3NUZXh0PWEuY3NzVGV4dD1jLnRyaW0oKTthLnBhcmVudCYmKGM9Yi5zdWJzdHJpbmcoYS5wcmV2aW91cz9hLnByZXZpb3VzLmVuZDphLnBhcmVudC5zdGFydCxhLnN0YXJ0LTEpLGM9QWUoYyksYz1jLnJlcGxhY2UoQmUsIiAiKSxjPWMuc3Vic3RyaW5nKGMubGFzdEluZGV4T2YoIjsiKSsxKSxjPWEucGFyc2VkU2VsZWN0b3I9YS5zZWxlY3Rvcj1jLnRyaW0oKSxhLmF0UnVsZT0wPT09Yy5pbmRleE9mKCJAIiksYS5hdFJ1bGU/MD09PWMuaW5kZXhPZigiQG1lZGlhIik/YS50eXBlPUNlOmMubWF0Y2goRGUpJiYoYS50eXBlPUVlLGEua2V5ZnJhbWVzTmFtZT1hLnNlbGVjdG9yLnNwbGl0KEJlKS5wb3AoKSk6YS50eXBlPTA9PT1jLmluZGV4T2YoIi0tIik/RmU6R2UpO2lmKGM9YS5ydWxlcylmb3IodmFyIGQ9MCxlPWMubGVuZ3RoLGY9dm9pZCAwO2Q8ZSYmKGY9Y1tkXSk7ZCsrKXplKGYsCmIpO3JldHVybiBhfWZ1bmN0aW9uIEFlKGEpe3JldHVybiBhLnJlcGxhY2UoL1xcKFswLTlhLWZdezEsNn0pXHMvZ2ksZnVuY3Rpb24oYSxjKXthPWM7Zm9yKGM9Ni1hLmxlbmd0aDtjLS07KWE9IjAiK2E7cmV0dXJuIlxcIithfSl9CmZ1bmN0aW9uIEhlKGEsYixjKXtjPXZvaWQgMD09PWM/IiI6Yzt2YXIgZD0iIjtpZihhLmNzc1RleHR8fGEucnVsZXMpe3ZhciBlPWEucnVsZXMsZjtpZihmPWUpZj1lWzBdLGY9IShmJiZmLnNlbGVjdG9yJiYwPT09Zi5zZWxlY3Rvci5pbmRleE9mKCItLSIpKTtpZihmKXtmPTA7Zm9yKHZhciBnPWUubGVuZ3RoLGg9dm9pZCAwO2Y8ZyYmKGg9ZVtmXSk7ZisrKWQ9SGUoaCxiLGQpfWVsc2UgYj9iPWEuY3NzVGV4dDooYj1hLmNzc1RleHQsYj1iLnJlcGxhY2UoSWUsIiIpLnJlcGxhY2UoSmUsIiIpLGI9Yi5yZXBsYWNlKEtlLCIiKS5yZXBsYWNlKExlLCIiKSksKGQ9Yi50cmltKCkpJiYoZD0iICAiK2QrIlxuIil9ZCYmKGEuc2VsZWN0b3ImJihjKz1hLnNlbGVjdG9yKyIge1xuIiksYys9ZCxhLnNlbGVjdG9yJiYoYys9In1cblxuIikpO3JldHVybiBjfQp2YXIgR2U9MSxFZT03LENlPTQsRmU9MUUzLHhlPS9cL1wqW14qXSpcKisoW14vKl1bXipdKlwqKykqXC8vZ2ltLHllPS9AaW1wb3J0W147XSo7L2dpbSxJZT0vKD86XlteO1wtXHN9XSspPy0tW147e31dKj86W157fTtdKj8oPzpbO1xuXXwkKS9naW0sSmU9Lyg/Ol5bXjtcLVxzfV0rKT8tLVteO3t9XSo/Oltee307XSo/e1tefV0qP30oPzpbO1xuXXwkKT8vZ2ltLEtlPS9AYXBwbHlccypcKD9bXik7XSpcKT9ccyooPzpbO1xuXXwkKT8vZ2ltLExlPS9bXjs6XSo/OlteO10qP3ZhclwoW147XSpcKSg/Ols7XG5dfCQpPy9naW0sRGU9L15AW15cc10qa2V5ZnJhbWVzLyxCZT0vXHMrL2c7dmFyIE89ISh3aW5kb3cuU2hhZHlET00mJndpbmRvdy5TaGFkeURPTS5pblVzZSksTWU7ZnVuY3Rpb24gTmUoYSl7TWU9YSYmYS5zaGltY3NzcHJvcGVydGllcz8hMTpPfHwhKG5hdmlnYXRvci51c2VyQWdlbnQubWF0Y2goL0FwcGxlV2ViS2l0XC82MDF8RWRnZVwvMTUvKXx8IXdpbmRvdy5DU1N8fCFDU1Muc3VwcG9ydHN8fCFDU1Muc3VwcG9ydHMoImJveC1zaGFkb3ciLCIwIDAgMCB2YXIoLS1mb28pIikpfXZhciBPZTt3aW5kb3cuU2hhZHlDU1MmJnZvaWQgMCE9PXdpbmRvdy5TaGFkeUNTUy5jc3NCdWlsZCYmKE9lPXdpbmRvdy5TaGFkeUNTUy5jc3NCdWlsZCk7dmFyIFBlPSEoIXdpbmRvdy5TaGFkeUNTU3x8IXdpbmRvdy5TaGFkeUNTUy5kaXNhYmxlUnVudGltZSk7CndpbmRvdy5TaGFkeUNTUyYmdm9pZCAwIT09d2luZG93LlNoYWR5Q1NTLm5hdGl2ZUNzcz9NZT13aW5kb3cuU2hhZHlDU1MubmF0aXZlQ3NzOndpbmRvdy5TaGFkeUNTUz8oTmUod2luZG93LlNoYWR5Q1NTKSx3aW5kb3cuU2hhZHlDU1M9dm9pZCAwKTpOZSh3aW5kb3cuV2ViQ29tcG9uZW50cyYmd2luZG93LldlYkNvbXBvbmVudHMuZmxhZ3MpO3ZhciBRPU1lLFFlPU9lO3ZhciBSZT0vKD86XnxbO1xze11ccyopKC0tW1x3LV0qPylccyo6XHMqKD86KCg/OicoPzpcXCd8LikqPyd8Iig/OlxcInwuKSo/InxcKFteKV0qP1wpfFtefTt7XSkrKXxceyhbXn1dKilcfSg/Oig/PVs7XHN9XSl8JCkpL2dpLFNlPS8oPzpefFxXKylAYXBwbHlccypcKD8oW14pO1xuXSopXCk/L2dpLFRlPS8oLS1bXHctXSspXHMqKFs6LDspXXwkKS9naSxVZT0vKGFuaW1hdGlvblxzKjopfChhbmltYXRpb24tbmFtZVxzKjopLywkZT0vQG1lZGlhXHMoLiopLyxhZj0vXHtbXn1dKlx9L2c7dmFyIGJmPW5ldyBTZXQ7ZnVuY3Rpb24gY2YoYSxiKXtpZighYSlyZXR1cm4iIjsic3RyaW5nIj09PXR5cGVvZiBhJiYoYT13ZShhKSk7YiYmZGYoYSxiKTtyZXR1cm4gSGUoYSxRKX1mdW5jdGlvbiBlZihhKXshYS5fX2Nzc1J1bGVzJiZhLnRleHRDb250ZW50JiYoYS5fX2Nzc1J1bGVzPXdlKGEudGV4dENvbnRlbnQpKTtyZXR1cm4gYS5fX2Nzc1J1bGVzfHxudWxsfWZ1bmN0aW9uIGZmKGEpe3JldHVybiEhYS5wYXJlbnQmJmEucGFyZW50LnR5cGU9PT1FZX1mdW5jdGlvbiBkZihhLGIsYyxkKXtpZihhKXt2YXIgZT0hMSxmPWEudHlwZTtpZihkJiZmPT09Q2Upe3ZhciBnPWEuc2VsZWN0b3IubWF0Y2goJGUpO2cmJih3aW5kb3cubWF0Y2hNZWRpYShnWzFdKS5tYXRjaGVzfHwoZT0hMCkpfWY9PT1HZT9iKGEpOmMmJmY9PT1FZT9jKGEpOmY9PT1GZSYmKGU9ITApO2lmKChhPWEucnVsZXMpJiYhZSlmb3IoZT0wLGY9YS5sZW5ndGgsZz12b2lkIDA7ZTxmJiYoZz1hW2VdKTtlKyspZGYoZyxiLGMsZCl9fQpmdW5jdGlvbiBnZihhLGIsYyxkKXt2YXIgZT1kb2N1bWVudC5jcmVhdGVFbGVtZW50KCJzdHlsZSIpO2ImJmUuc2V0QXR0cmlidXRlKCJzY29wZSIsYik7ZS50ZXh0Q29udGVudD1hO2hmKGUsYyxkKTtyZXR1cm4gZX12YXIgamY9bnVsbDtmdW5jdGlvbiBrZihhKXthPWRvY3VtZW50LmNyZWF0ZUNvbW1lbnQoIiBTaGFkeSBET00gc3R5bGVzIGZvciAiK2ErIiAiKTt2YXIgYj1kb2N1bWVudC5oZWFkO2IuaW5zZXJ0QmVmb3JlKGEsKGpmP2pmLm5leHRTaWJsaW5nOm51bGwpfHxiLmZpcnN0Q2hpbGQpO3JldHVybiBqZj1hfWZ1bmN0aW9uIGhmKGEsYixjKXtiPWJ8fGRvY3VtZW50LmhlYWQ7Yi5pbnNlcnRCZWZvcmUoYSxjJiZjLm5leHRTaWJsaW5nfHxiLmZpcnN0Q2hpbGQpO2pmP2EuY29tcGFyZURvY3VtZW50UG9zaXRpb24oamYpPT09Tm9kZS5ET0NVTUVOVF9QT1NJVElPTl9QUkVDRURJTkcmJihqZj1hKTpqZj1hfQpmdW5jdGlvbiBsZihhLGIpe2Zvcih2YXIgYz0wLGQ9YS5sZW5ndGg7YjxkO2IrKylpZigiKCI9PT1hW2JdKWMrKztlbHNlIGlmKCIpIj09PWFbYl0mJjA9PT0tLWMpcmV0dXJuIGI7cmV0dXJuLTF9ZnVuY3Rpb24gbWYoYSxiKXt2YXIgYz1hLmluZGV4T2YoInZhcigiKTtpZigtMT09PWMpcmV0dXJuIGIoYSwiIiwiIiwiIik7dmFyIGQ9bGYoYSxjKzMpLGU9YS5zdWJzdHJpbmcoYys0LGQpO2M9YS5zdWJzdHJpbmcoMCxjKTthPW1mKGEuc3Vic3RyaW5nKGQrMSksYik7ZD1lLmluZGV4T2YoIiwiKTtyZXR1cm4tMT09PWQ/YihjLGUudHJpbSgpLCIiLGEpOmIoYyxlLnN1YnN0cmluZygwLGQpLnRyaW0oKSxlLnN1YnN0cmluZyhkKzEpLnRyaW0oKSxhKX1mdW5jdGlvbiBuZihhLGIpe08/YS5zZXRBdHRyaWJ1dGUoImNsYXNzIixiKTp3aW5kb3cuU2hhZHlET00ubmF0aXZlTWV0aG9kcy5zZXRBdHRyaWJ1dGUuY2FsbChhLCJjbGFzcyIsYil9CnZhciBvZj13aW5kb3cuU2hhZHlET00mJndpbmRvdy5TaGFkeURPTS53cmFwfHxmdW5jdGlvbihhKXtyZXR1cm4gYX07ZnVuY3Rpb24gcGYoYSl7dmFyIGI9YS5sb2NhbE5hbWUsYz0iIjtiPy0xPGIuaW5kZXhPZigiLSIpfHwoYz1iLGI9YS5nZXRBdHRyaWJ1dGUmJmEuZ2V0QXR0cmlidXRlKCJpcyIpfHwiIik6KGI9YS5pcyxjPWEuZXh0ZW5kcyk7cmV0dXJue2lzOmIsTTpjfX1mdW5jdGlvbiBxZihhKXtmb3IodmFyIGI9W10sYz0iIixkPTA7MDw9ZCYmZDxhLmxlbmd0aDtkKyspaWYoIigiPT09YVtkXSl7dmFyIGU9bGYoYSxkKTtjKz1hLnNsaWNlKGQsZSsxKTtkPWV9ZWxzZSIsIj09PWFbZF0/KGIucHVzaChjKSxjPSIiKTpjKz1hW2RdO2MmJmIucHVzaChjKTtyZXR1cm4gYn0KZnVuY3Rpb24gcmYoYSl7aWYodm9pZCAwIT09UWUpcmV0dXJuIFFlO2lmKHZvaWQgMD09PWEuX19jc3NCdWlsZCl7dmFyIGI9YS5nZXRBdHRyaWJ1dGUoImNzcy1idWlsZCIpO2lmKGIpYS5fX2Nzc0J1aWxkPWI7ZWxzZXthOntiPSJ0ZW1wbGF0ZSI9PT1hLmxvY2FsTmFtZT9hLmNvbnRlbnQuZmlyc3RDaGlsZDphLmZpcnN0Q2hpbGQ7aWYoYiBpbnN0YW5jZW9mIENvbW1lbnQmJihiPWIudGV4dENvbnRlbnQudHJpbSgpLnNwbGl0KCI6IiksImNzcy1idWlsZCI9PT1iWzBdKSl7Yj1iWzFdO2JyZWFrIGF9Yj0iIn1pZigiIiE9PWIpe3ZhciBjPSJ0ZW1wbGF0ZSI9PT1hLmxvY2FsTmFtZT9hLmNvbnRlbnQuZmlyc3RDaGlsZDphLmZpcnN0Q2hpbGQ7Yy5wYXJlbnROb2RlLnJlbW92ZUNoaWxkKGMpfWEuX19jc3NCdWlsZD1ifX1yZXR1cm4gYS5fX2Nzc0J1aWxkfHwiIn0KZnVuY3Rpb24gc2YoYSl7YT12b2lkIDA9PT1hPyIiOmE7cmV0dXJuIiIhPT1hJiZRP08/InNoYWRvdyI9PT1hOiJzaGFkeSI9PT1hOiExfTtmdW5jdGlvbiB0Zigpe31mdW5jdGlvbiB1ZihhLGIpe3ZmKFIsYSxmdW5jdGlvbihhKXt3ZihhLGJ8fCIiKX0pfWZ1bmN0aW9uIHZmKGEsYixjKXtiLm5vZGVUeXBlPT09Tm9kZS5FTEVNRU5UX05PREUmJmMoYik7dmFyIGQ7InRlbXBsYXRlIj09PWIubG9jYWxOYW1lP2Q9KGIuY29udGVudHx8Yi5fY29udGVudHx8YikuY2hpbGROb2RlczpkPWIuY2hpbGRyZW58fGIuY2hpbGROb2RlcztpZihkKWZvcihiPTA7YjxkLmxlbmd0aDtiKyspdmYoYSxkW2JdLGMpfQpmdW5jdGlvbiB3ZihhLGIsYyl7aWYoYilpZihhLmNsYXNzTGlzdCljPyhhLmNsYXNzTGlzdC5yZW1vdmUoInN0eWxlLXNjb3BlIiksYS5jbGFzc0xpc3QucmVtb3ZlKGIpKTooYS5jbGFzc0xpc3QuYWRkKCJzdHlsZS1zY29wZSIpLGEuY2xhc3NMaXN0LmFkZChiKSk7ZWxzZSBpZihhLmdldEF0dHJpYnV0ZSl7dmFyIGQ9YS5nZXRBdHRyaWJ1dGUoImNsYXNzIik7Yz9kJiYoYj1kLnJlcGxhY2UoInN0eWxlLXNjb3BlIiwiIikucmVwbGFjZShiLCIiKSxuZihhLGIpKTpuZihhLChkP2QrIiAiOiIiKSsic3R5bGUtc2NvcGUgIitiKX19ZnVuY3Rpb24geGYoYSxiLGMpe3ZmKFIsYSxmdW5jdGlvbihhKXt3ZihhLGIsITApO3dmKGEsYyl9KX1mdW5jdGlvbiB5ZihhLGIpe3ZmKFIsYSxmdW5jdGlvbihhKXt3ZihhLGJ8fCIiLCEwKX0pfQpmdW5jdGlvbiB6ZihhLGIsYyxkLGUpe3ZhciBmPVI7ZT12b2lkIDA9PT1lPyIiOmU7IiI9PT1lJiYoT3x8InNoYWR5Ij09PSh2b2lkIDA9PT1kPyIiOmQpP2U9Y2YoYixjKTooYT1wZihhKSxlPUFmKGYsYixhLmlzLGEuTSxjKSsiXG5cbiIpKTtyZXR1cm4gZS50cmltKCl9ZnVuY3Rpb24gQWYoYSxiLGMsZCxlKXt2YXIgZj1CZihjLGQpO2M9Yz8iLiIrYzoiIjtyZXR1cm4gY2YoYixmdW5jdGlvbihiKXtiLmN8fChiLnNlbGVjdG9yPWIuaj1DZihhLGIsYS5iLGMsZiksYi5jPSEwKTtlJiZlKGIsYyxmKX0pfWZ1bmN0aW9uIEJmKGEsYil7cmV0dXJuIGI/Iltpcz0iK2ErIl0iOmF9CmZ1bmN0aW9uIENmKGEsYixjLGQsZSl7dmFyIGY9cWYoYi5zZWxlY3Rvcik7aWYoIWZmKGIpKXtiPTA7Zm9yKHZhciBnPWYubGVuZ3RoLGg9dm9pZCAwO2I8ZyYmKGg9ZltiXSk7YisrKWZbYl09Yy5jYWxsKGEsaCxkLGUpfXJldHVybiBmLmZpbHRlcihmdW5jdGlvbihhKXtyZXR1cm4hIWF9KS5qb2luKCIsIil9ZnVuY3Rpb24gRGYoYSl7cmV0dXJuIGEucmVwbGFjZShFZixmdW5jdGlvbihhLGMsZCl7LTE8ZC5pbmRleE9mKCIrIik/ZD1kLnJlcGxhY2UoL1wrL2csIl9fXyIpOi0xPGQuaW5kZXhPZigiX19fIikmJihkPWQucmVwbGFjZSgvX19fL2csIisiKSk7cmV0dXJuIjoiK2MrIigiK2QrIikifSl9CmZ1bmN0aW9uIEZmKGEpe2Zvcih2YXIgYj1bXSxjO2M9YS5tYXRjaChHZik7KXt2YXIgZD1jLmluZGV4LGU9bGYoYSxkKTtpZigtMT09PWUpdGhyb3cgRXJyb3IoYy5pbnB1dCsiIHNlbGVjdG9yIG1pc3NpbmcgJyknIik7Yz1hLnNsaWNlKGQsZSsxKTthPWEucmVwbGFjZShjLCJcdWUwMDAiKTtiLnB1c2goYyl9cmV0dXJue2hhOmEsbWF0Y2hlczpifX1mdW5jdGlvbiBIZihhLGIpe3ZhciBjPWEuc3BsaXQoIlx1ZTAwMCIpO3JldHVybiBiLnJlZHVjZShmdW5jdGlvbihhLGIsZil7cmV0dXJuIGErYitjW2YrMV19LGNbMF0pfQp0Zi5wcm90b3R5cGUuYj1mdW5jdGlvbihhLGIsYyl7dmFyIGQ9ITE7YT1hLnRyaW0oKTt2YXIgZT1FZi50ZXN0KGEpO2UmJihhPWEucmVwbGFjZShFZixmdW5jdGlvbihhLGIsYyl7cmV0dXJuIjoiK2IrIigiK2MucmVwbGFjZSgvXHMvZywiIikrIikifSksYT1EZihhKSk7dmFyIGY9R2YudGVzdChhKTtpZihmKXt2YXIgZz1GZihhKTthPWcuaGE7Zz1nLm1hdGNoZXN9YT1hLnJlcGxhY2UoSWYsIjpob3N0ICQxIik7YT1hLnJlcGxhY2UoSmYsZnVuY3Rpb24oYSxlLGYpe2R8fChhPUtmKGYsZSxiLGMpLGQ9ZHx8YS5zdG9wLGU9YS5DYSxmPWEudmFsdWUpO3JldHVybiBlK2Z9KTtmJiYoYT1IZihhLGcpKTtlJiYoYT1EZihhKSk7cmV0dXJuIGF9OwpmdW5jdGlvbiBLZihhLGIsYyxkKXt2YXIgZT1hLmluZGV4T2YoIjo6c2xvdHRlZCIpOzA8PWEuaW5kZXhPZigiOmhvc3QiKT9hPUxmKGEsZCk6MCE9PWUmJihhPWM/TWYoYSxjKTphKTtjPSExOzA8PWUmJihiPSIiLGM9ITApO2lmKGMpe3ZhciBmPSEwO2MmJihhPWEucmVwbGFjZShOZixmdW5jdGlvbihhLGIpe3JldHVybiIgPiAiK2J9KSl9YT1hLnJlcGxhY2UoT2YsZnVuY3Rpb24oYSxiLGMpe3JldHVybidbZGlyPSInK2MrJyJdICcrYisiLCAiK2IrJ1tkaXI9IicrYysnIl0nfSk7cmV0dXJue3ZhbHVlOmEsQ2E6YixzdG9wOmZ9fQpmdW5jdGlvbiBNZihhLGIpe2E9YS5zcGxpdCgvKFxbLis/XF0pLyk7Zm9yKHZhciBjPVtdLGQ9MDtkPGEubGVuZ3RoO2QrKylpZigxPT09ZCUyKWMucHVzaChhW2RdKTtlbHNle3ZhciBlPWFbZF07aWYoIiIhPT1lfHxkIT09YS5sZW5ndGgtMSllPWUuc3BsaXQoIjoiKSxlWzBdKz1iLGMucHVzaChlLmpvaW4oIjoiKSl9cmV0dXJuIGMuam9pbigiIil9ZnVuY3Rpb24gTGYoYSxiKXt2YXIgYz1hLm1hdGNoKFBmKTtyZXR1cm4oYz1jJiZjWzJdLnRyaW0oKXx8IiIpP2NbMF0ubWF0Y2goUWYpP2EucmVwbGFjZShQZixmdW5jdGlvbihhLGMsZil7cmV0dXJuIGIrZn0pOmMuc3BsaXQoUWYpWzBdPT09Yj9jOiJzaG91bGRfbm90X21hdGNoIjphLnJlcGxhY2UoIjpob3N0IixiKX1mdW5jdGlvbiBSZihhKXsiOnJvb3QiPT09YS5zZWxlY3RvciYmKGEuc2VsZWN0b3I9Imh0bWwiKX0KdGYucHJvdG90eXBlLmM9ZnVuY3Rpb24oYSl7cmV0dXJuIGEubWF0Y2goIjpob3N0Iik/IiI6YS5tYXRjaCgiOjpzbG90dGVkIik/dGhpcy5iKGEsIjpub3QoLnN0eWxlLXNjb3BlKSIpOk1mKGEudHJpbSgpLCI6bm90KC5zdHlsZS1zY29wZSkiKX07cC5PYmplY3QuZGVmaW5lUHJvcGVydGllcyh0Zi5wcm90b3R5cGUse2E6e2NvbmZpZ3VyYWJsZTohMCxlbnVtZXJhYmxlOiEwLGdldDpmdW5jdGlvbigpe3JldHVybiJzdHlsZS1zY29wZSJ9fX0pOwp2YXIgRWY9LzoobnRoWy1cd10rKVwoKFteKV0rKVwpLyxKZj0vKF58W1xzPit+XSspKCg/OlxbLis/XF18W15ccz4rfj1bXSkrKS9nLFFmPS9bWy46IypdLyxJZj0vXig6OnNsb3R0ZWQpLyxQZj0vKDpob3N0KSg/OlwoKCg/OlwoW14pKF0qXCl8W14pKF0qKSs/KVwpKS8sTmY9Lyg/Ojo6c2xvdHRlZCkoPzpcKCgoPzpcKFteKShdKlwpfFteKShdKikrPylcKSkvLE9mPS8oLiopOmRpclwoKD86KGx0cnxydGwpKVwpLyxHZj0vOig/Om1hdGNoZXN8YW55fC0oPzp3ZWJraXR8bW96KS1hbnkpLyxSPW5ldyB0ZjtmdW5jdGlvbiBTZihhLGIsYyxkLGUpe3RoaXMuQT1hfHxudWxsO3RoaXMuYj1ifHxudWxsO3RoaXMuZmE9Y3x8W107dGhpcy5vPW51bGw7dGhpcy5jc3NCdWlsZD1lfHwiIjt0aGlzLk09ZHx8IiI7dGhpcy5hPXRoaXMucz10aGlzLnc9bnVsbH1mdW5jdGlvbiBUKGEpe3JldHVybiBhP2EuX19zdHlsZUluZm86bnVsbH1mdW5jdGlvbiBUZihhLGIpe3JldHVybiBhLl9fc3R5bGVJbmZvPWJ9U2YucHJvdG90eXBlLmM9ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5BfTtTZi5wcm90b3R5cGUuX2dldFN0eWxlUnVsZXM9U2YucHJvdG90eXBlLmM7ZnVuY3Rpb24gVWYoYSl7dmFyIGI9dGhpcy5tYXRjaGVzfHx0aGlzLm1hdGNoZXNTZWxlY3Rvcnx8dGhpcy5tb3pNYXRjaGVzU2VsZWN0b3J8fHRoaXMubXNNYXRjaGVzU2VsZWN0b3J8fHRoaXMub01hdGNoZXNTZWxlY3Rvcnx8dGhpcy53ZWJraXRNYXRjaGVzU2VsZWN0b3I7cmV0dXJuIGImJmIuY2FsbCh0aGlzLGEpfXZhciBWZj1uYXZpZ2F0b3IudXNlckFnZW50Lm1hdGNoKCJUcmlkZW50Iik7ZnVuY3Rpb24gV2YoKXt9ZnVuY3Rpb24gWGYoYSl7dmFyIGI9e30sYz1bXSxkPTA7ZGYoYSxmdW5jdGlvbihhKXtZZihhKTthLmluZGV4PWQrKzthPWEuaS5jc3NUZXh0O2Zvcih2YXIgYztjPVRlLmV4ZWMoYSk7KXt2YXIgZT1jWzFdOyI6IiE9PWNbMl0mJihiW2VdPSEwKX19LGZ1bmN0aW9uKGEpe2MucHVzaChhKX0pO2EuYj1jO2E9W107Zm9yKHZhciBlIGluIGIpYS5wdXNoKGUpO3JldHVybiBhfQpmdW5jdGlvbiBZZihhKXtpZighYS5pKXt2YXIgYj17fSxjPXt9O1pmKGEsYykmJihiLnY9YyxhLnJ1bGVzPW51bGwpO2IuY3NzVGV4dD1hLnBhcnNlZENzc1RleHQucmVwbGFjZShhZiwiIikucmVwbGFjZShSZSwiIik7YS5pPWJ9fWZ1bmN0aW9uIFpmKGEsYil7dmFyIGM9YS5pO2lmKGMpe2lmKGMudilyZXR1cm4gT2JqZWN0LmFzc2lnbihiLGMudiksITB9ZWxzZXtjPWEucGFyc2VkQ3NzVGV4dDtmb3IodmFyIGQ7YT1SZS5leGVjKGMpOyl7ZD0oYVsyXXx8YVszXSkudHJpbSgpO2lmKCJpbmhlcml0IiE9PWR8fCJ1bnNldCIhPT1kKWJbYVsxXS50cmltKCldPWQ7ZD0hMH1yZXR1cm4gZH19CmZ1bmN0aW9uICRmKGEsYixjKXtiJiYoYj0wPD1iLmluZGV4T2YoIjsiKT9hZyhhLGIsYyk6bWYoYixmdW5jdGlvbihiLGUsZixnKXtpZighZSlyZXR1cm4gYitnOyhlPSRmKGEsY1tlXSxjKSkmJiJpbml0aWFsIiE9PWU/ImFwcGx5LXNoaW0taW5oZXJpdCI9PT1lJiYoZT0iaW5oZXJpdCIpOmU9JGYoYSxjW2ZdfHxmLGMpfHxmO3JldHVybiBiKyhlfHwiIikrZ30pKTtyZXR1cm4gYiYmYi50cmltKCl8fCIifQpmdW5jdGlvbiBhZyhhLGIsYyl7Yj1iLnNwbGl0KCI7Iik7Zm9yKHZhciBkPTAsZSxmO2Q8Yi5sZW5ndGg7ZCsrKWlmKGU9YltkXSl7U2UubGFzdEluZGV4PTA7aWYoZj1TZS5leGVjKGUpKWU9JGYoYSxjW2ZbMV1dLGMpO2Vsc2UgaWYoZj1lLmluZGV4T2YoIjoiKSwtMSE9PWYpe3ZhciBnPWUuc3Vic3RyaW5nKGYpO2c9Zy50cmltKCk7Zz0kZihhLGcsYyl8fGc7ZT1lLnN1YnN0cmluZygwLGYpK2d9YltkXT1lJiZlLmxhc3RJbmRleE9mKCI7Iik9PT1lLmxlbmd0aC0xP2Uuc2xpY2UoMCwtMSk6ZXx8IiJ9cmV0dXJuIGIuam9pbigiOyIpfQpmdW5jdGlvbiBiZyhhLGIpe3ZhciBjPXt9LGQ9W107ZGYoYSxmdW5jdGlvbihhKXthLml8fFlmKGEpO3ZhciBlPWEuanx8YS5wYXJzZWRTZWxlY3RvcjtiJiZhLmkudiYmZSYmVWYuY2FsbChiLGUpJiYoWmYoYSxjKSxhPWEuaW5kZXgsZT1wYXJzZUludChhLzMyLDEwKSxkW2VdPShkW2VdfHwwKXwxPDxhJTMyKX0sbnVsbCwhMCk7cmV0dXJue3Y6YyxrZXk6ZH19CmZ1bmN0aW9uIGNnKGEsYixjLGQpe2IuaXx8WWYoYik7aWYoYi5pLnYpe3ZhciBlPXBmKGEpO2E9ZS5pcztlPWUuTTtlPWE/QmYoYSxlKToiaHRtbCI7dmFyIGY9Yi5wYXJzZWRTZWxlY3RvcixnPSI6aG9zdCA+ICoiPT09Znx8Imh0bWwiPT09ZixoPTA9PT1mLmluZGV4T2YoIjpob3N0IikmJiFnOyJzaGFkeSI9PT1jJiYoZz1mPT09ZSsiID4gKi4iK2V8fC0xIT09Zi5pbmRleE9mKCJodG1sIiksaD0hZyYmMD09PWYuaW5kZXhPZihlKSk7aWYoZ3x8aCljPWUsaCYmKGIuanx8KGIuaj1DZihSLGIsUi5iLGE/Ii4iK2E6IiIsZSkpLGM9Yi5qfHxlKSxkKHtoYTpjLElhOmgsWmE6Z30pfX1mdW5jdGlvbiBkZyhhLGIsYyl7dmFyIGQ9e30sZT17fTtkZihiLGZ1bmN0aW9uKGIpe2NnKGEsYixjLGZ1bmN0aW9uKGMpe1VmLmNhbGwoYS5fZWxlbWVudHx8YSxjLmhhKSYmKGMuSWE/WmYoYixkKTpaZihiLGUpKX0pfSxudWxsLCEwKTtyZXR1cm57UmE6ZSxIYTpkfX0KZnVuY3Rpb24gZWcoYSxiLGMsZCl7dmFyIGU9cGYoYiksZj1CZihlLmlzLGUuTSksZz1uZXcgUmVnRXhwKCIoPzpefFteLiNbOl0pIisoYi5leHRlbmRzPyJcXCIrZi5zbGljZSgwLC0xKSsiXFxdIjpmKSsiKCR8Wy46W1xccz4rfl0pIiksaD1UKGIpO2U9aC5BO2g9aC5jc3NCdWlsZDt2YXIgaz1mZyhlLGQpO3JldHVybiB6ZihiLGUsZnVuY3Rpb24oYil7dmFyIGU9IiI7Yi5pfHxZZihiKTtiLmkuY3NzVGV4dCYmKGU9YWcoYSxiLmkuY3NzVGV4dCxjKSk7Yi5jc3NUZXh0PWU7aWYoIU8mJiFmZihiKSYmYi5jc3NUZXh0KXt2YXIgaD1lPWIuY3NzVGV4dDtudWxsPT1iLm5hJiYoYi5uYT1VZS50ZXN0KGUpKTtpZihiLm5hKWlmKG51bGw9PWIuVSl7Yi5VPVtdO2Zvcih2YXIgbCBpbiBrKWg9a1tsXSxoPWgoZSksZSE9PWgmJihlPWgsYi5VLnB1c2gobCkpfWVsc2V7Zm9yKGw9MDtsPGIuVS5sZW5ndGg7KytsKWg9a1tiLlVbbF1dLGU9aChlKTtoPWV9Yi5jc3NUZXh0PWg7Yi5qPWIuanx8CmIuc2VsZWN0b3I7ZT0iLiIrZDtsPXFmKGIuaik7aD0wO2Zvcih2YXIgTT1sLmxlbmd0aCxVPXZvaWQgMDtoPE0mJihVPWxbaF0pO2grKylsW2hdPVUubWF0Y2goZyk/VS5yZXBsYWNlKGYsZSk6ZSsiICIrVTtiLnNlbGVjdG9yPWwuam9pbigiLCIpfX0saCl9ZnVuY3Rpb24gZmcoYSxiKXthPWEuYjt2YXIgYz17fTtpZighTyYmYSlmb3IodmFyIGQ9MCxlPWFbZF07ZDxhLmxlbmd0aDtlPWFbKytkXSl7dmFyIGY9ZSxnPWI7Zi5mPW5ldyBSZWdFeHAoIlxcYiIrZi5rZXlmcmFtZXNOYW1lKyIoPyFcXEJ8LSkiLCJnIik7Zi5hPWYua2V5ZnJhbWVzTmFtZSsiLSIrZztmLmo9Zi5qfHxmLnNlbGVjdG9yO2Yuc2VsZWN0b3I9Zi5qLnJlcGxhY2UoZi5rZXlmcmFtZXNOYW1lLGYuYSk7Y1tlLmtleWZyYW1lc05hbWVdPWdnKGUpfXJldHVybiBjfWZ1bmN0aW9uIGdnKGEpe3JldHVybiBmdW5jdGlvbihiKXtyZXR1cm4gYi5yZXBsYWNlKGEuZixhLmEpfX0KZnVuY3Rpb24gaGcoYSxiKXt2YXIgYz1pZyxkPWVmKGEpO2EudGV4dENvbnRlbnQ9Y2YoZCxmdW5jdGlvbihhKXt2YXIgZD1hLmNzc1RleHQ9YS5wYXJzZWRDc3NUZXh0O2EuaSYmYS5pLmNzc1RleHQmJihkPWQucmVwbGFjZShJZSwiIikucmVwbGFjZShKZSwiIiksYS5jc3NUZXh0PWFnKGMsZCxiKSl9KX1wLk9iamVjdC5kZWZpbmVQcm9wZXJ0aWVzKFdmLnByb3RvdHlwZSx7YTp7Y29uZmlndXJhYmxlOiEwLGVudW1lcmFibGU6ITAsZ2V0OmZ1bmN0aW9uKCl7cmV0dXJuIngtc2NvcGUifX19KTt2YXIgaWc9bmV3IFdmO3ZhciBqZz17fSxrZz13aW5kb3cuY3VzdG9tRWxlbWVudHM7aWYoa2cmJiFPJiYhUGUpe3ZhciBsZz1rZy5kZWZpbmU7a2cuZGVmaW5lPWZ1bmN0aW9uKGEsYixjKXtqZ1thXXx8KGpnW2FdPWtmKGEpKTtsZy5jYWxsKGtnLGEsYixjKX19O2Z1bmN0aW9uIG1nKCl7dGhpcy5jYWNoZT17fX1tZy5wcm90b3R5cGUuc3RvcmU9ZnVuY3Rpb24oYSxiLGMsZCl7dmFyIGU9dGhpcy5jYWNoZVthXXx8W107ZS5wdXNoKHt2OmIsc3R5bGVFbGVtZW50OmMsczpkfSk7MTAwPGUubGVuZ3RoJiZlLnNoaWZ0KCk7dGhpcy5jYWNoZVthXT1lfTtmdW5jdGlvbiBuZygpe312YXIgb2c9bmV3IFJlZ0V4cChSLmErIlxccyooW15cXHNdKikiKTtmdW5jdGlvbiBwZyhhKXtyZXR1cm4oYT0oYS5jbGFzc0xpc3QmJmEuY2xhc3NMaXN0LnZhbHVlP2EuY2xhc3NMaXN0LnZhbHVlOmEuZ2V0QXR0cmlidXRlKCJjbGFzcyIpfHwiIikubWF0Y2gob2cpKT9hWzFdOiIifWZ1bmN0aW9uIHFnKGEpe3ZhciBiPW9mKGEpLmdldFJvb3ROb2RlKCk7cmV0dXJuIGI9PT1hfHxiPT09YS5vd25lckRvY3VtZW50PyIiOihhPWIuaG9zdCk/cGYoYSkuaXM6IiJ9CmZ1bmN0aW9uIHJnKGEpe2Zvcih2YXIgYj0wO2I8YS5sZW5ndGg7YisrKXt2YXIgYz1hW2JdO2lmKGMudGFyZ2V0IT09ZG9jdW1lbnQuZG9jdW1lbnRFbGVtZW50JiZjLnRhcmdldCE9PWRvY3VtZW50LmhlYWQpZm9yKHZhciBkPTA7ZDxjLmFkZGVkTm9kZXMubGVuZ3RoO2QrKyl7dmFyIGU9Yy5hZGRlZE5vZGVzW2RdO2lmKGUubm9kZVR5cGU9PT1Ob2RlLkVMRU1FTlRfTk9ERSl7dmFyIGY9ZS5nZXRSb290Tm9kZSgpLGc9cGcoZSk7aWYoZyYmZj09PWUub3duZXJEb2N1bWVudCYmKCJzdHlsZSIhPT1lLmxvY2FsTmFtZSYmInRlbXBsYXRlIiE9PWUubG9jYWxOYW1lfHwiIj09PXJmKGUpKSl5ZihlLGcpO2Vsc2UgaWYoZiBpbnN0YW5jZW9mIFNoYWRvd1Jvb3QpZm9yKGY9cWcoZSksZiE9PWcmJnhmKGUsZyxmKSxlPXdpbmRvdy5TaGFkeURPTS5uYXRpdmVNZXRob2RzLnF1ZXJ5U2VsZWN0b3JBbGwuY2FsbChlLCI6bm90KC4iK1IuYSsiKSIpLGc9MDtnPGUubGVuZ3RoO2crKyl7Zj1lW2ddOwp2YXIgaD1xZyhmKTtoJiZ3ZihmLGgpfX19fX0KaWYoIShPfHx3aW5kb3cuU2hhZHlET00mJndpbmRvdy5TaGFkeURPTS5oYW5kbGVzRHluYW1pY1Njb3BpbmcpKXt2YXIgc2c9bmV3IE11dGF0aW9uT2JzZXJ2ZXIocmcpLHRnPWZ1bmN0aW9uKGEpe3NnLm9ic2VydmUoYSx7Y2hpbGRMaXN0OiEwLHN1YnRyZWU6ITB9KX07aWYod2luZG93LmN1c3RvbUVsZW1lbnRzJiYhd2luZG93LmN1c3RvbUVsZW1lbnRzLnBvbHlmaWxsV3JhcEZsdXNoQ2FsbGJhY2spdGcoZG9jdW1lbnQpO2Vsc2V7dmFyIHVnPWZ1bmN0aW9uKCl7dGcoZG9jdW1lbnQuYm9keSl9O3dpbmRvdy5IVE1MSW1wb3J0cz93aW5kb3cuSFRNTEltcG9ydHMud2hlblJlYWR5KHVnKTpyZXF1ZXN0QW5pbWF0aW9uRnJhbWUoZnVuY3Rpb24oKXtpZigibG9hZGluZyI9PT1kb2N1bWVudC5yZWFkeVN0YXRlKXt2YXIgYT1mdW5jdGlvbigpe3VnKCk7ZG9jdW1lbnQucmVtb3ZlRXZlbnRMaXN0ZW5lcigicmVhZHlzdGF0ZWNoYW5nZSIsYSl9O2RvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoInJlYWR5c3RhdGVjaGFuZ2UiLAphKX1lbHNlIHVnKCl9KX1uZz1mdW5jdGlvbigpe3JnKHNnLnRha2VSZWNvcmRzKCkpfX12YXIgdmc9bmc7dmFyIHdnPXt9O3ZhciB4Zz1Qcm9taXNlLnJlc29sdmUoKTtmdW5jdGlvbiB5ZyhhKXtpZihhPXdnW2FdKWEuX2FwcGx5U2hpbUN1cnJlbnRWZXJzaW9uPWEuX2FwcGx5U2hpbUN1cnJlbnRWZXJzaW9ufHwwLGEuX2FwcGx5U2hpbVZhbGlkYXRpbmdWZXJzaW9uPWEuX2FwcGx5U2hpbVZhbGlkYXRpbmdWZXJzaW9ufHwwLGEuX2FwcGx5U2hpbU5leHRWZXJzaW9uPShhLl9hcHBseVNoaW1OZXh0VmVyc2lvbnx8MCkrMX1mdW5jdGlvbiB6ZyhhKXtyZXR1cm4gYS5fYXBwbHlTaGltQ3VycmVudFZlcnNpb249PT1hLl9hcHBseVNoaW1OZXh0VmVyc2lvbn1mdW5jdGlvbiBBZyhhKXthLl9hcHBseVNoaW1WYWxpZGF0aW5nVmVyc2lvbj1hLl9hcHBseVNoaW1OZXh0VmVyc2lvbjthLl92YWxpZGF0aW5nfHwoYS5fdmFsaWRhdGluZz0hMCx4Zy50aGVuKGZ1bmN0aW9uKCl7YS5fYXBwbHlTaGltQ3VycmVudFZlcnNpb249YS5fYXBwbHlTaGltTmV4dFZlcnNpb247YS5fdmFsaWRhdGluZz0hMX0pKX07dmFyIEJnPXt9LENnPW5ldyBtZztmdW5jdGlvbiBZKCl7dGhpcy5sPXt9O3RoaXMuYz1kb2N1bWVudC5kb2N1bWVudEVsZW1lbnQ7dmFyIGE9bmV3IHZlO2EucnVsZXM9W107dGhpcy5mPVRmKHRoaXMuYyxuZXcgU2YoYSkpO3RoaXMuZz0hMTt0aGlzLmI9dGhpcy5hPW51bGx9bj1ZLnByb3RvdHlwZTtuLmZsdXNoPWZ1bmN0aW9uKCl7dmcoKX07bi5GYT1mdW5jdGlvbihhKXtyZXR1cm4gZWYoYSl9O24uVmE9ZnVuY3Rpb24oYSl7cmV0dXJuIGNmKGEpfTtuLnByZXBhcmVUZW1wbGF0ZT1mdW5jdGlvbihhLGIsYyl7dGhpcy5wcmVwYXJlVGVtcGxhdGVEb20oYSxiKTt0aGlzLnByZXBhcmVUZW1wbGF0ZVN0eWxlcyhhLGIsYyl9OwpuLnByZXBhcmVUZW1wbGF0ZVN0eWxlcz1mdW5jdGlvbihhLGIsYyl7aWYoIWEuX3ByZXBhcmVkJiYhUGUpe098fGpnW2JdfHwoamdbYl09a2YoYikpO2EuX3ByZXBhcmVkPSEwO2EubmFtZT1iO2EuZXh0ZW5kcz1jO3dnW2JdPWE7dmFyIGQ9cmYoYSksZT1zZihkKTtjPXtpczpiLGV4dGVuZHM6Y307Zm9yKHZhciBmPVtdLGc9YS5jb250ZW50LnF1ZXJ5U2VsZWN0b3JBbGwoInN0eWxlIiksaD0wO2g8Zy5sZW5ndGg7aCsrKXt2YXIgaz1nW2hdO2lmKGsuaGFzQXR0cmlidXRlKCJzaGFkeS11bnNjb3BlZCIpKXtpZighTyl7dmFyIGw9ay50ZXh0Q29udGVudDtiZi5oYXMobCl8fChiZi5hZGQobCksbD1rLmNsb25lTm9kZSghMCksZG9jdW1lbnQuaGVhZC5hcHBlbmRDaGlsZChsKSk7ay5wYXJlbnROb2RlLnJlbW92ZUNoaWxkKGspfX1lbHNlIGYucHVzaChrLnRleHRDb250ZW50KSxrLnBhcmVudE5vZGUucmVtb3ZlQ2hpbGQoayl9Zj1mLmpvaW4oIiIpLnRyaW0oKSsoQmdbYl18fCIiKTsKRGcodGhpcyk7aWYoIWUpe2lmKGc9IWQpZz1TZS50ZXN0KGYpfHxSZS50ZXN0KGYpLFNlLmxhc3RJbmRleD0wLFJlLmxhc3RJbmRleD0wO2g9d2UoZik7ZyYmUSYmdGhpcy5hJiZ0aGlzLmEudHJhbnNmb3JtUnVsZXMoaCxiKTthLl9zdHlsZUFzdD1ofWc9W107UXx8KGc9WGYoYS5fc3R5bGVBc3QpKTtpZighZy5sZW5ndGh8fFEpaD1PP2EuY29udGVudDpudWxsLGI9amdbYl18fG51bGwsZD16ZihjLGEuX3N0eWxlQXN0LG51bGwsZCxlP2Y6IiIpLGQ9ZC5sZW5ndGg/Z2YoZCxjLmlzLGgsYik6bnVsbCxhLl9zdHlsZT1kO2EuYT1nfX07bi5RYT1mdW5jdGlvbihhLGIpe0JnW2JdPWEuam9pbigiICIpfTtuLnByZXBhcmVUZW1wbGF0ZURvbT1mdW5jdGlvbihhLGIpe2lmKCFQZSl7dmFyIGM9cmYoYSk7T3x8InNoYWR5Ij09PWN8fGEuX2RvbVByZXBhcmVkfHwoYS5fZG9tUHJlcGFyZWQ9ITAsdWYoYS5jb250ZW50LGIpKX19OwpmdW5jdGlvbiBFZyhhKXt2YXIgYj1wZihhKSxjPWIuaXM7Yj1iLk07dmFyIGQ9amdbY118fG51bGwsZT13Z1tjXTtpZihlKXtjPWUuX3N0eWxlQXN0O3ZhciBmPWUuYTtlPXJmKGUpO2I9bmV3IFNmKGMsZCxmLGIsZSk7VGYoYSxiKTtyZXR1cm4gYn19ZnVuY3Rpb24gRmcoYSl7IWEuYiYmd2luZG93LlNoYWR5Q1NTJiZ3aW5kb3cuU2hhZHlDU1MuQ3VzdG9tU3R5bGVJbnRlcmZhY2UmJihhLmI9d2luZG93LlNoYWR5Q1NTLkN1c3RvbVN0eWxlSW50ZXJmYWNlLGEuYi50cmFuc2Zvcm1DYWxsYmFjaz1mdW5jdGlvbihiKXthLnJhKGIpfSxhLmIudmFsaWRhdGVDYWxsYmFjaz1mdW5jdGlvbigpe3JlcXVlc3RBbmltYXRpb25GcmFtZShmdW5jdGlvbigpeyhhLmIuZW5xdWV1ZWR8fGEuZykmJmEuZmx1c2hDdXN0b21TdHlsZXMoKX0pfSl9CmZ1bmN0aW9uIERnKGEpeyFhLmEmJndpbmRvdy5TaGFkeUNTUyYmd2luZG93LlNoYWR5Q1NTLkFwcGx5U2hpbSYmKGEuYT13aW5kb3cuU2hhZHlDU1MuQXBwbHlTaGltLGEuYS5pbnZhbGlkQ2FsbGJhY2s9eWcpO0ZnKGEpfQpuLmZsdXNoQ3VzdG9tU3R5bGVzPWZ1bmN0aW9uKCl7aWYoIVBlJiYoRGcodGhpcyksdGhpcy5iKSl7dmFyIGE9dGhpcy5iLnByb2Nlc3NTdHlsZXMoKTtpZih0aGlzLmIuZW5xdWV1ZWQmJiFzZih0aGlzLmYuY3NzQnVpbGQpKXtpZihRKXtpZighdGhpcy5mLmNzc0J1aWxkKWZvcih2YXIgYj0wO2I8YS5sZW5ndGg7YisrKXt2YXIgYz10aGlzLmIuZ2V0U3R5bGVGb3JDdXN0b21TdHlsZShhW2JdKTtpZihjJiZRJiZ0aGlzLmEpe3ZhciBkPWVmKGMpO0RnKHRoaXMpO3RoaXMuYS50cmFuc2Zvcm1SdWxlcyhkKTtjLnRleHRDb250ZW50PWNmKGQpfX19ZWxzZXtHZyh0aGlzLHRoaXMuYyx0aGlzLmYpO2ZvcihiPTA7YjxhLmxlbmd0aDtiKyspKGM9dGhpcy5iLmdldFN0eWxlRm9yQ3VzdG9tU3R5bGUoYVtiXSkpJiZoZyhjLHRoaXMuZi53KTt0aGlzLmcmJnRoaXMuc3R5bGVEb2N1bWVudCgpfXRoaXMuYi5lbnF1ZXVlZD0hMX19fTsKbi5zdHlsZUVsZW1lbnQ9ZnVuY3Rpb24oYSxiKXtpZihQZSl7aWYoYil7VChhKXx8VGYoYSxuZXcgU2YobnVsbCkpO3ZhciBjPVQoYSk7Yy5vPWMub3x8e307T2JqZWN0LmFzc2lnbihjLm8sYik7SGcodGhpcyxhLGMpfX1lbHNlIGlmKGM9VChhKXx8RWcoYSkpaWYoYSE9PXRoaXMuYyYmKHRoaXMuZz0hMCksYiYmKGMubz1jLm98fHt9LE9iamVjdC5hc3NpZ24oYy5vLGIpKSxRKUhnKHRoaXMsYSxjKTtlbHNlIGlmKHRoaXMuZmx1c2goKSxHZyh0aGlzLGEsYyksYy5mYSYmYy5mYS5sZW5ndGgpe2I9cGYoYSkuaXM7dmFyIGQ7YTp7aWYoZD1DZy5jYWNoZVtiXSlmb3IodmFyIGU9ZC5sZW5ndGgtMTswPD1lO2UtLSl7dmFyIGY9ZFtlXTtiOnt2YXIgZz1jLmZhO2Zvcih2YXIgaD0wO2g8Zy5sZW5ndGg7aCsrKXt2YXIgaz1nW2hdO2lmKGYudltrXSE9PWMud1trXSl7Zz0hMTticmVhayBifX1nPSEwfWlmKGcpe2Q9ZjticmVhayBhfX1kPXZvaWQgMH1nPWQ/ZC5zdHlsZUVsZW1lbnQ6bnVsbDsKZT1jLnM7KGY9ZCYmZC5zKXx8KGY9dGhpcy5sW2JdPSh0aGlzLmxbYl18fDApKzEsZj1iKyItIitmKTtjLnM9ZjtmPWMucztoPWlnO2g9Zz9nLnRleHRDb250ZW50fHwiIjplZyhoLGEsYy53LGYpO2s9VChhKTt2YXIgbD1rLmE7bCYmIU8mJmwhPT1nJiYobC5fdXNlQ291bnQtLSwwPj1sLl91c2VDb3VudCYmbC5wYXJlbnROb2RlJiZsLnBhcmVudE5vZGUucmVtb3ZlQ2hpbGQobCkpO08/ay5hPyhrLmEudGV4dENvbnRlbnQ9aCxnPWsuYSk6aCYmKGc9Z2YoaCxmLGEuc2hhZG93Um9vdCxrLmIpKTpnP2cucGFyZW50Tm9kZXx8KFZmJiYtMTxoLmluZGV4T2YoIkBtZWRpYSIpJiYoZy50ZXh0Q29udGVudD1oKSxoZihnLG51bGwsay5iKSk6aCYmKGc9Z2YoaCxmLG51bGwsay5iKSk7ZyYmKGcuX3VzZUNvdW50PWcuX3VzZUNvdW50fHwwLGsuYSE9ZyYmZy5fdXNlQ291bnQrKyxrLmE9Zyk7Zj1nO098fChnPWMucyxrPWg9YS5nZXRBdHRyaWJ1dGUoImNsYXNzIil8fCIiLGUmJihrPWgucmVwbGFjZShuZXcgUmVnRXhwKCJcXHMqeC1zY29wZVxccyoiKwplKyJcXHMqIiwiZyIpLCIgIikpLGsrPShrPyIgIjoiIikrIngtc2NvcGUgIitnLGghPT1rJiZuZihhLGspKTtkfHxDZy5zdG9yZShiLGMudyxmLGMucyl9fTsKZnVuY3Rpb24gSGcoYSxiLGMpe3ZhciBkPXBmKGIpLmlzO2lmKGMubyl7dmFyIGU9Yy5vLGY7Zm9yKGYgaW4gZSludWxsPT09Zj9iLnN0eWxlLnJlbW92ZVByb3BlcnR5KGYpOmIuc3R5bGUuc2V0UHJvcGVydHkoZixlW2ZdKX1lPXdnW2RdO2lmKCEoIWUmJmIhPT1hLmN8fGUmJiIiIT09cmYoZSkpJiZlJiZlLl9zdHlsZSYmIXpnKGUpKXtpZih6ZyhlKXx8ZS5fYXBwbHlTaGltVmFsaWRhdGluZ1ZlcnNpb24hPT1lLl9hcHBseVNoaW1OZXh0VmVyc2lvbilEZyhhKSxhLmEmJmEuYS50cmFuc2Zvcm1SdWxlcyhlLl9zdHlsZUFzdCxkKSxlLl9zdHlsZS50ZXh0Q29udGVudD16ZihiLGMuQSksQWcoZSk7TyYmKGE9Yi5zaGFkb3dSb290KSYmKGE9YS5xdWVyeVNlbGVjdG9yKCJzdHlsZSIpKSYmKGEudGV4dENvbnRlbnQ9emYoYixjLkEpKTtjLkE9ZS5fc3R5bGVBc3R9fQpmdW5jdGlvbiBJZyhhLGIpe3JldHVybihiPW9mKGIpLmdldFJvb3ROb2RlKCkuaG9zdCk/VChiKXx8RWcoYik/YjpJZyhhLGIpOmEuY31mdW5jdGlvbiBHZyhhLGIsYyl7dmFyIGQ9SWcoYSxiKSxlPVQoZCksZj1lLnc7ZD09PWEuY3x8Znx8KEdnKGEsZCxlKSxmPWUudyk7YT1PYmplY3QuY3JlYXRlKGZ8fG51bGwpO2Q9ZGcoYixjLkEsYy5jc3NCdWlsZCk7Yj1iZyhlLkEsYikudjtPYmplY3QuYXNzaWduKGEsZC5IYSxiLGQuUmEpO2I9Yy5vO2Zvcih2YXIgZyBpbiBiKWlmKChlPWJbZ10pfHwwPT09ZSlhW2ddPWU7Zz1pZztiPU9iamVjdC5nZXRPd25Qcm9wZXJ0eU5hbWVzKGEpO2ZvcihlPTA7ZTxiLmxlbmd0aDtlKyspZD1iW2VdLGFbZF09JGYoZyxhW2RdLGEpO2Mudz1hfW4uc3R5bGVEb2N1bWVudD1mdW5jdGlvbihhKXt0aGlzLnN0eWxlU3VidHJlZSh0aGlzLmMsYSl9OwpuLnN0eWxlU3VidHJlZT1mdW5jdGlvbihhLGIpe3ZhciBjPW9mKGEpLGQ9Yy5zaGFkb3dSb290OyhkfHxhPT09dGhpcy5jKSYmdGhpcy5zdHlsZUVsZW1lbnQoYSxiKTtpZihhPWQmJihkLmNoaWxkcmVufHxkLmNoaWxkTm9kZXMpKWZvcihjPTA7YzxhLmxlbmd0aDtjKyspdGhpcy5zdHlsZVN1YnRyZWUoYVtjXSk7ZWxzZSBpZihjPWMuY2hpbGRyZW58fGMuY2hpbGROb2Rlcylmb3IoYT0wO2E8Yy5sZW5ndGg7YSsrKXRoaXMuc3R5bGVTdWJ0cmVlKGNbYV0pfTsKbi5yYT1mdW5jdGlvbihhKXt2YXIgYj10aGlzLGM9cmYoYSk7YyE9PXRoaXMuZi5jc3NCdWlsZCYmKHRoaXMuZi5jc3NCdWlsZD1jKTtpZighc2YoYykpe3ZhciBkPWVmKGEpO2RmKGQsZnVuY3Rpb24oYSl7aWYoTylSZihhKTtlbHNle3ZhciBkPVI7YS5zZWxlY3Rvcj1hLnBhcnNlZFNlbGVjdG9yO1JmKGEpO2Euc2VsZWN0b3I9YS5qPUNmKGQsYSxkLmMsdm9pZCAwLHZvaWQgMCl9USYmIiI9PT1jJiYoRGcoYiksYi5hJiZiLmEudHJhbnNmb3JtUnVsZShhKSl9KTtRP2EudGV4dENvbnRlbnQ9Y2YoZCk6dGhpcy5mLkEucnVsZXMucHVzaChkKX19O24uZ2V0Q29tcHV0ZWRTdHlsZVZhbHVlPWZ1bmN0aW9uKGEsYil7dmFyIGM7UXx8KGM9KFQoYSl8fFQoSWcodGhpcyxhKSkpLndbYl0pO3JldHVybihjPWN8fHdpbmRvdy5nZXRDb21wdXRlZFN0eWxlKGEpLmdldFByb3BlcnR5VmFsdWUoYikpP2MudHJpbSgpOiIifTsKbi5VYT1mdW5jdGlvbihhLGIpe3ZhciBjPW9mKGEpLmdldFJvb3ROb2RlKCk7Yj1iP2Iuc3BsaXQoL1xzLyk6W107Yz1jLmhvc3QmJmMuaG9zdC5sb2NhbE5hbWU7aWYoIWMpe3ZhciBkPWEuZ2V0QXR0cmlidXRlKCJjbGFzcyIpO2lmKGQpe2Q9ZC5zcGxpdCgvXHMvKTtmb3IodmFyIGU9MDtlPGQubGVuZ3RoO2UrKylpZihkW2VdPT09Ui5hKXtjPWRbZSsxXTticmVha319fWMmJmIucHVzaChSLmEsYyk7UXx8KGM9VChhKSkmJmMucyYmYi5wdXNoKGlnLmEsYy5zKTtuZihhLGIuam9pbigiICIpKX07bi5CYT1mdW5jdGlvbihhKXtyZXR1cm4gVChhKX07bi5UYT1mdW5jdGlvbihhLGIpe3dmKGEsYil9O24uV2E9ZnVuY3Rpb24oYSxiKXt3ZihhLGIsITApfTtuLlNhPWZ1bmN0aW9uKGEpe3JldHVybiBxZyhhKX07bi5EYT1mdW5jdGlvbihhKXtyZXR1cm4gcGcoYSl9O1kucHJvdG90eXBlLmZsdXNoPVkucHJvdG90eXBlLmZsdXNoO1kucHJvdG90eXBlLnByZXBhcmVUZW1wbGF0ZT1ZLnByb3RvdHlwZS5wcmVwYXJlVGVtcGxhdGU7ClkucHJvdG90eXBlLnN0eWxlRWxlbWVudD1ZLnByb3RvdHlwZS5zdHlsZUVsZW1lbnQ7WS5wcm90b3R5cGUuc3R5bGVEb2N1bWVudD1ZLnByb3RvdHlwZS5zdHlsZURvY3VtZW50O1kucHJvdG90eXBlLnN0eWxlU3VidHJlZT1ZLnByb3RvdHlwZS5zdHlsZVN1YnRyZWU7WS5wcm90b3R5cGUuZ2V0Q29tcHV0ZWRTdHlsZVZhbHVlPVkucHJvdG90eXBlLmdldENvbXB1dGVkU3R5bGVWYWx1ZTtZLnByb3RvdHlwZS5zZXRFbGVtZW50Q2xhc3M9WS5wcm90b3R5cGUuVWE7WS5wcm90b3R5cGUuX3N0eWxlSW5mb0Zvck5vZGU9WS5wcm90b3R5cGUuQmE7WS5wcm90b3R5cGUudHJhbnNmb3JtQ3VzdG9tU3R5bGVGb3JEb2N1bWVudD1ZLnByb3RvdHlwZS5yYTtZLnByb3RvdHlwZS5nZXRTdHlsZUFzdD1ZLnByb3RvdHlwZS5GYTtZLnByb3RvdHlwZS5zdHlsZUFzdFRvU3RyaW5nPVkucHJvdG90eXBlLlZhO1kucHJvdG90eXBlLmZsdXNoQ3VzdG9tU3R5bGVzPVkucHJvdG90eXBlLmZsdXNoQ3VzdG9tU3R5bGVzOwpZLnByb3RvdHlwZS5zY29wZU5vZGU9WS5wcm90b3R5cGUuVGE7WS5wcm90b3R5cGUudW5zY29wZU5vZGU9WS5wcm90b3R5cGUuV2E7WS5wcm90b3R5cGUuc2NvcGVGb3JOb2RlPVkucHJvdG90eXBlLlNhO1kucHJvdG90eXBlLmN1cnJlbnRTY29wZUZvck5vZGU9WS5wcm90b3R5cGUuRGE7WS5wcm90b3R5cGUucHJlcGFyZUFkb3B0ZWRDc3NUZXh0PVkucHJvdG90eXBlLlFhO09iamVjdC5kZWZpbmVQcm9wZXJ0aWVzKFkucHJvdG90eXBlLHtuYXRpdmVTaGFkb3c6e2dldDpmdW5jdGlvbigpe3JldHVybiBPfX0sbmF0aXZlQ3NzOntnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gUX19fSk7dmFyIFo9bmV3IFksSmcsS2c7d2luZG93LlNoYWR5Q1NTJiYoSmc9d2luZG93LlNoYWR5Q1NTLkFwcGx5U2hpbSxLZz13aW5kb3cuU2hhZHlDU1MuQ3VzdG9tU3R5bGVJbnRlcmZhY2UpOwp3aW5kb3cuU2hhZHlDU1M9e1Njb3BpbmdTaGltOloscHJlcGFyZVRlbXBsYXRlOmZ1bmN0aW9uKGEsYixjKXtaLmZsdXNoQ3VzdG9tU3R5bGVzKCk7Wi5wcmVwYXJlVGVtcGxhdGUoYSxiLGMpfSxwcmVwYXJlVGVtcGxhdGVEb206ZnVuY3Rpb24oYSxiKXtaLnByZXBhcmVUZW1wbGF0ZURvbShhLGIpfSxwcmVwYXJlVGVtcGxhdGVTdHlsZXM6ZnVuY3Rpb24oYSxiLGMpe1ouZmx1c2hDdXN0b21TdHlsZXMoKTtaLnByZXBhcmVUZW1wbGF0ZVN0eWxlcyhhLGIsYyl9LHN0eWxlU3VidHJlZTpmdW5jdGlvbihhLGIpe1ouZmx1c2hDdXN0b21TdHlsZXMoKTtaLnN0eWxlU3VidHJlZShhLGIpfSxzdHlsZUVsZW1lbnQ6ZnVuY3Rpb24oYSl7Wi5mbHVzaEN1c3RvbVN0eWxlcygpO1ouc3R5bGVFbGVtZW50KGEpfSxzdHlsZURvY3VtZW50OmZ1bmN0aW9uKGEpe1ouZmx1c2hDdXN0b21TdHlsZXMoKTtaLnN0eWxlRG9jdW1lbnQoYSl9LGZsdXNoQ3VzdG9tU3R5bGVzOmZ1bmN0aW9uKCl7Wi5mbHVzaEN1c3RvbVN0eWxlcygpfSwKZ2V0Q29tcHV0ZWRTdHlsZVZhbHVlOmZ1bmN0aW9uKGEsYil7cmV0dXJuIFouZ2V0Q29tcHV0ZWRTdHlsZVZhbHVlKGEsYil9LG5hdGl2ZUNzczpRLG5hdGl2ZVNoYWRvdzpPLGNzc0J1aWxkOlFlLGRpc2FibGVSdW50aW1lOlBlfTtKZyYmKHdpbmRvdy5TaGFkeUNTUy5BcHBseVNoaW09SmcpO0tnJiYod2luZG93LlNoYWR5Q1NTLkN1c3RvbVN0eWxlSW50ZXJmYWNlPUtnKTt2YXIgTGc9d2luZG93LmN1c3RvbUVsZW1lbnRzLE1nPXdpbmRvdy5IVE1MSW1wb3J0cyxOZz13aW5kb3cuSFRNTFRlbXBsYXRlRWxlbWVudDt3aW5kb3cuV2ViQ29tcG9uZW50cz13aW5kb3cuV2ViQ29tcG9uZW50c3x8e307aWYoTGcmJkxnLnBvbHlmaWxsV3JhcEZsdXNoQ2FsbGJhY2spe3ZhciBPZyxQZz1mdW5jdGlvbigpe2lmKE9nKXtOZy5DJiZOZy5DKHdpbmRvdy5kb2N1bWVudCk7dmFyIGE9T2c7T2c9bnVsbDthKCk7cmV0dXJuITB9fSxRZz1NZy53aGVuUmVhZHk7TGcucG9seWZpbGxXcmFwRmx1c2hDYWxsYmFjayhmdW5jdGlvbihhKXtPZz1hO1FnKFBnKX0pO01nLndoZW5SZWFkeT1mdW5jdGlvbihhKXtRZyhmdW5jdGlvbigpe1BnKCk/TWcud2hlblJlYWR5KGEpOmEoKX0pfX0KTWcud2hlblJlYWR5KGZ1bmN0aW9uKCl7cmVxdWVzdEFuaW1hdGlvbkZyYW1lKGZ1bmN0aW9uKCl7d2luZG93LldlYkNvbXBvbmVudHMucmVhZHk9ITA7ZG9jdW1lbnQuZGlzcGF0Y2hFdmVudChuZXcgQ3VzdG9tRXZlbnQoIldlYkNvbXBvbmVudHNSZWFkeSIse2J1YmJsZXM6ITB9KSl9KX0pO3ZhciBSZz1kb2N1bWVudC5jcmVhdGVFbGVtZW50KCJzdHlsZSIpO1JnLnRleHRDb250ZW50PSJib2R5IHt0cmFuc2l0aW9uOiBvcGFjaXR5IGVhc2UtaW4gMC4yczsgfSBcbmJvZHlbdW5yZXNvbHZlZF0ge29wYWNpdHk6IDA7IGRpc3BsYXk6IGJsb2NrOyBvdmVyZmxvdzogaGlkZGVuOyBwb3NpdGlvbjogcmVsYXRpdmU7IH0gXG4iO3ZhciBTZz1kb2N1bWVudC5xdWVyeVNlbGVjdG9yKCJoZWFkIik7U2cuaW5zZXJ0QmVmb3JlKFJnLFNnLmZpcnN0Q2hpbGQpO30pLmNhbGwodGhpcyk7CgoKPC9zY3JpcHQ+PHNjcmlwdD4oZnVuY3Rpb24oKXsvKgoKQ29weXJpZ2h0IChjKSAyMDE3IFRoZSBQb2x5bWVyIFByb2plY3QgQXV0aG9ycy4gQWxsIHJpZ2h0cyByZXNlcnZlZC4KVGhpcyBjb2RlIG1heSBvbmx5IGJlIHVzZWQgdW5kZXIgdGhlIEJTRCBzdHlsZSBsaWNlbnNlIGZvdW5kIGF0IGh0dHA6Ly9wb2x5bWVyLmdpdGh1Yi5pby9MSUNFTlNFLnR4dApUaGUgY29tcGxldGUgc2V0IG9mIGF1dGhvcnMgbWF5IGJlIGZvdW5kIGF0IGh0dHA6Ly9wb2x5bWVyLmdpdGh1Yi5pby9BVVRIT1JTLnR4dApUaGUgY29tcGxldGUgc2V0IG9mIGNvbnRyaWJ1dG9ycyBtYXkgYmUgZm91bmQgYXQgaHR0cDovL3BvbHltZXIuZ2l0aHViLmlvL0NPTlRSSUJVVE9SUy50eHQKQ29kZSBkaXN0cmlidXRlZCBieSBHb29nbGUgYXMgcGFydCBvZiB0aGUgcG9seW1lciBwcm9qZWN0IGlzIGFsc28Kc3ViamVjdCB0byBhbiBhZGRpdGlvbmFsIElQIHJpZ2h0cyBncmFudCBmb3VuZCBhdCBodHRwOi8vcG9seW1lci5naXRodWIuaW8vUEFURU5UUy50eHQKKi8KJ3VzZSBzdHJpY3QnO3ZhciBsPSEod2luZG93LlNoYWR5RE9NJiZ3aW5kb3cuU2hhZHlET00uaW5Vc2UpLHA7ZnVuY3Rpb24gcihhKXtwPWEmJmEuc2hpbWNzc3Byb3BlcnRpZXM/ITE6bHx8IShuYXZpZ2F0b3IudXNlckFnZW50Lm1hdGNoKC9BcHBsZVdlYktpdFwvNjAxfEVkZ2VcLzE1Lyl8fCF3aW5kb3cuQ1NTfHwhQ1NTLnN1cHBvcnRzfHwhQ1NTLnN1cHBvcnRzKCJib3gtc2hhZG93IiwiMCAwIDAgdmFyKC0tZm9vKSIpKX12YXIgdDt3aW5kb3cuU2hhZHlDU1MmJnZvaWQgMCE9PXdpbmRvdy5TaGFkeUNTUy5jc3NCdWlsZCYmKHQ9d2luZG93LlNoYWR5Q1NTLmNzc0J1aWxkKTt2YXIgYWE9ISghd2luZG93LlNoYWR5Q1NTfHwhd2luZG93LlNoYWR5Q1NTLmRpc2FibGVSdW50aW1lKTsKd2luZG93LlNoYWR5Q1NTJiZ2b2lkIDAhPT13aW5kb3cuU2hhZHlDU1MubmF0aXZlQ3NzP3A9d2luZG93LlNoYWR5Q1NTLm5hdGl2ZUNzczp3aW5kb3cuU2hhZHlDU1M/KHIod2luZG93LlNoYWR5Q1NTKSx3aW5kb3cuU2hhZHlDU1M9dm9pZCAwKTpyKHdpbmRvdy5XZWJDb21wb25lbnRzJiZ3aW5kb3cuV2ViQ29tcG9uZW50cy5mbGFncyk7dmFyIHU9cCx2PXQ7ZnVuY3Rpb24gdygpe3RoaXMuZW5kPXRoaXMuc3RhcnQ9MDt0aGlzLnJ1bGVzPXRoaXMucGFyZW50PXRoaXMucHJldmlvdXM9bnVsbDt0aGlzLmNzc1RleHQ9dGhpcy5wYXJzZWRDc3NUZXh0PSIiO3RoaXMuYXRSdWxlPSExO3RoaXMudHlwZT0wO3RoaXMucGFyc2VkU2VsZWN0b3I9dGhpcy5zZWxlY3Rvcj10aGlzLmtleWZyYW1lc05hbWU9IiJ9CmZ1bmN0aW9uIHgoYSl7YT1hLnJlcGxhY2UoYmEsIiIpLnJlcGxhY2UoY2EsIiIpO3ZhciBiPXksYz1hLGU9bmV3IHc7ZS5zdGFydD0wO2UuZW5kPWMubGVuZ3RoO2Zvcih2YXIgZD1lLGY9MCxnPWMubGVuZ3RoO2Y8ZztmKyspaWYoInsiPT09Y1tmXSl7ZC5ydWxlc3x8KGQucnVsZXM9W10pO3ZhciBoPWQsaz1oLnJ1bGVzW2gucnVsZXMubGVuZ3RoLTFdfHxudWxsO2Q9bmV3IHc7ZC5zdGFydD1mKzE7ZC5wYXJlbnQ9aDtkLnByZXZpb3VzPWs7aC5ydWxlcy5wdXNoKGQpfWVsc2UifSI9PT1jW2ZdJiYoZC5lbmQ9ZisxLGQ9ZC5wYXJlbnR8fGUpO3JldHVybiBiKGUsYSl9CmZ1bmN0aW9uIHkoYSxiKXt2YXIgYz1iLnN1YnN0cmluZyhhLnN0YXJ0LGEuZW5kLTEpO2EucGFyc2VkQ3NzVGV4dD1hLmNzc1RleHQ9Yy50cmltKCk7YS5wYXJlbnQmJihjPWIuc3Vic3RyaW5nKGEucHJldmlvdXM/YS5wcmV2aW91cy5lbmQ6YS5wYXJlbnQuc3RhcnQsYS5zdGFydC0xKSxjPWRhKGMpLGM9Yy5yZXBsYWNlKHosIiAiKSxjPWMuc3Vic3RyaW5nKGMubGFzdEluZGV4T2YoIjsiKSsxKSxjPWEucGFyc2VkU2VsZWN0b3I9YS5zZWxlY3Rvcj1jLnRyaW0oKSxhLmF0UnVsZT0wPT09Yy5pbmRleE9mKCJAIiksYS5hdFJ1bGU/MD09PWMuaW5kZXhPZigiQG1lZGlhIik/YS50eXBlPUE6Yy5tYXRjaChlYSkmJihhLnR5cGU9QixhLmtleWZyYW1lc05hbWU9YS5zZWxlY3Rvci5zcGxpdCh6KS5wb3AoKSk6YS50eXBlPTA9PT1jLmluZGV4T2YoIi0tIik/QzpEKTtpZihjPWEucnVsZXMpZm9yKHZhciBlPTAsZD1jLmxlbmd0aCxmPXZvaWQgMDtlPGQmJihmPWNbZV0pO2UrKyl5KGYsYik7CnJldHVybiBhfWZ1bmN0aW9uIGRhKGEpe3JldHVybiBhLnJlcGxhY2UoL1xcKFswLTlhLWZdezEsNn0pXHMvZ2ksZnVuY3Rpb24oYSxjKXthPWM7Zm9yKGM9Ni1hLmxlbmd0aDtjLS07KWE9IjAiK2E7cmV0dXJuIlxcIithfSl9CmZ1bmN0aW9uIEUoYSxiLGMpe2M9dm9pZCAwPT09Yz8iIjpjO3ZhciBlPSIiO2lmKGEuY3NzVGV4dHx8YS5ydWxlcyl7dmFyIGQ9YS5ydWxlcyxmO2lmKGY9ZClmPWRbMF0sZj0hKGYmJmYuc2VsZWN0b3ImJjA9PT1mLnNlbGVjdG9yLmluZGV4T2YoIi0tIikpO2lmKGYpe2Y9MDtmb3IodmFyIGc9ZC5sZW5ndGgsaD12b2lkIDA7ZjxnJiYoaD1kW2ZdKTtmKyspZT1FKGgsYixlKX1lbHNlIGI/Yj1hLmNzc1RleHQ6KGI9YS5jc3NUZXh0LGI9Yi5yZXBsYWNlKGZhLCIiKS5yZXBsYWNlKGhhLCIiKSxiPWIucmVwbGFjZShpYSwiIikucmVwbGFjZShqYSwiIikpLChlPWIudHJpbSgpKSYmKGU9IiAgIitlKyJcbiIpfWUmJihhLnNlbGVjdG9yJiYoYys9YS5zZWxlY3RvcisiIHtcbiIpLGMrPWUsYS5zZWxlY3RvciYmKGMrPSJ9XG5cbiIpKTtyZXR1cm4gY30KdmFyIEQ9MSxCPTcsQT00LEM9MUUzLGJhPS9cL1wqW14qXSpcKisoW14vKl1bXipdKlwqKykqXC8vZ2ltLGNhPS9AaW1wb3J0W147XSo7L2dpbSxmYT0vKD86XlteO1wtXHN9XSspPy0tW147e31dKj86W157fTtdKj8oPzpbO1xuXXwkKS9naW0saGE9Lyg/Ol5bXjtcLVxzfV0rKT8tLVteO3t9XSo/Oltee307XSo/e1tefV0qP30oPzpbO1xuXXwkKT8vZ2ltLGlhPS9AYXBwbHlccypcKD9bXik7XSpcKT9ccyooPzpbO1xuXXwkKT8vZ2ltLGphPS9bXjs6XSo/OlteO10qP3ZhclwoW147XSpcKSg/Ols7XG5dfCQpPy9naW0sZWE9L15AW15cc10qa2V5ZnJhbWVzLyx6PS9ccysvZzt2YXIgRz0vKD86XnxbO1xze11ccyopKC0tW1x3LV0qPylccyo6XHMqKD86KCg/OicoPzpcXCd8LikqPyd8Iig/OlxcInwuKSo/InxcKFteKV0qP1wpfFtefTt7XSkrKXxceyhbXn1dKilcfSg/Oig/PVs7XHN9XSl8JCkpL2dpLEg9Lyg/Ol58XFcrKUBhcHBseVxzKlwoPyhbXik7XG5dKilcKT8vZ2ksa2E9L0BtZWRpYVxzKC4qKS87dmFyIEk9bmV3IFNldDtmdW5jdGlvbiBKKGEpe2lmKCFhKXJldHVybiIiOyJzdHJpbmciPT09dHlwZW9mIGEmJihhPXgoYSkpO3JldHVybiBFKGEsdSl9ZnVuY3Rpb24gSyhhKXshYS5fX2Nzc1J1bGVzJiZhLnRleHRDb250ZW50JiYoYS5fX2Nzc1J1bGVzPXgoYS50ZXh0Q29udGVudCkpO3JldHVybiBhLl9fY3NzUnVsZXN8fG51bGx9ZnVuY3Rpb24gTChhLGIsYyxlKXtpZihhKXt2YXIgZD0hMSxmPWEudHlwZTtpZihlJiZmPT09QSl7dmFyIGc9YS5zZWxlY3Rvci5tYXRjaChrYSk7ZyYmKHdpbmRvdy5tYXRjaE1lZGlhKGdbMV0pLm1hdGNoZXN8fChkPSEwKSl9Zj09PUQ/YihhKTpjJiZmPT09Qj9jKGEpOmY9PT1DJiYoZD0hMCk7aWYoKGE9YS5ydWxlcykmJiFkKWZvcihkPTAsZj1hLmxlbmd0aCxnPXZvaWQgMDtkPGYmJihnPWFbZF0pO2QrKylMKGcsYixjLGUpfX0KZnVuY3Rpb24gTShhLGIpe3ZhciBjPWEuaW5kZXhPZigidmFyKCIpO2lmKC0xPT09YylyZXR1cm4gYihhLCIiLCIiLCIiKTthOnt2YXIgZT0wO3ZhciBkPWMrMztmb3IodmFyIGY9YS5sZW5ndGg7ZDxmO2QrKylpZigiKCI9PT1hW2RdKWUrKztlbHNlIGlmKCIpIj09PWFbZF0mJjA9PT0tLWUpYnJlYWsgYTtkPS0xfWU9YS5zdWJzdHJpbmcoYys0LGQpO2M9YS5zdWJzdHJpbmcoMCxjKTthPU0oYS5zdWJzdHJpbmcoZCsxKSxiKTtkPWUuaW5kZXhPZigiLCIpO3JldHVybi0xPT09ZD9iKGMsZS50cmltKCksIiIsYSk6YihjLGUuc3Vic3RyaW5nKDAsZCkudHJpbSgpLGUuc3Vic3RyaW5nKGQrMSkudHJpbSgpLGEpfQpmdW5jdGlvbiBOKGEpe2lmKHZvaWQgMCE9PXYpcmV0dXJuIHY7aWYodm9pZCAwPT09YS5fX2Nzc0J1aWxkKXt2YXIgYj1hLmdldEF0dHJpYnV0ZSgiY3NzLWJ1aWxkIik7aWYoYilhLl9fY3NzQnVpbGQ9YjtlbHNle2E6e2I9InRlbXBsYXRlIj09PWEubG9jYWxOYW1lP2EuY29udGVudC5maXJzdENoaWxkOmEuZmlyc3RDaGlsZDtpZihiIGluc3RhbmNlb2YgQ29tbWVudCYmKGI9Yi50ZXh0Q29udGVudC50cmltKCkuc3BsaXQoIjoiKSwiY3NzLWJ1aWxkIj09PWJbMF0pKXtiPWJbMV07YnJlYWsgYX1iPSIifWlmKCIiIT09Yil7dmFyIGM9InRlbXBsYXRlIj09PWEubG9jYWxOYW1lP2EuY29udGVudC5maXJzdENoaWxkOmEuZmlyc3RDaGlsZDtjLnBhcmVudE5vZGUucmVtb3ZlQ2hpbGQoYyl9YS5fX2Nzc0J1aWxkPWJ9fXJldHVybiBhLl9fY3NzQnVpbGR8fCIifTt2YXIgbGE9LztccyovbSxtYT0vXlxzKihpbml0aWFsKXwoaW5oZXJpdClccyokLyxPPS9ccyohaW1wb3J0YW50LztmdW5jdGlvbiBQKCl7dGhpcy5hPXt9fVAucHJvdG90eXBlLnNldD1mdW5jdGlvbihhLGIpe2E9YS50cmltKCk7dGhpcy5hW2FdPXtoOmIsaTp7fX19O1AucHJvdG90eXBlLmdldD1mdW5jdGlvbihhKXthPWEudHJpbSgpO3JldHVybiB0aGlzLmFbYV18fG51bGx9O3ZhciBRPW51bGw7ZnVuY3Rpb24gUigpe3RoaXMuYj10aGlzLmM9bnVsbDt0aGlzLmE9bmV3IFB9Ui5wcm90b3R5cGUubz1mdW5jdGlvbihhKXthPUgudGVzdChhKXx8Ry50ZXN0KGEpO0gubGFzdEluZGV4PTA7Ry5sYXN0SW5kZXg9MDtyZXR1cm4gYX07ClIucHJvdG90eXBlLm09ZnVuY3Rpb24oYSxiKXtpZih2b2lkIDA9PT1hLl9nYXRoZXJlZFN0eWxlKXt2YXIgYz1bXTtmb3IodmFyIGU9YS5jb250ZW50LnF1ZXJ5U2VsZWN0b3JBbGwoInN0eWxlIiksZD0wO2Q8ZS5sZW5ndGg7ZCsrKXt2YXIgZj1lW2RdO2lmKGYuaGFzQXR0cmlidXRlKCJzaGFkeS11bnNjb3BlZCIpKXtpZighbCl7dmFyIGc9Zi50ZXh0Q29udGVudDtJLmhhcyhnKXx8KEkuYWRkKGcpLGc9Zi5jbG9uZU5vZGUoITApLGRvY3VtZW50LmhlYWQuYXBwZW5kQ2hpbGQoZykpO2YucGFyZW50Tm9kZS5yZW1vdmVDaGlsZChmKX19ZWxzZSBjLnB1c2goZi50ZXh0Q29udGVudCksZi5wYXJlbnROb2RlLnJlbW92ZUNoaWxkKGYpfShjPWMuam9pbigiIikudHJpbSgpKT8oZT1kb2N1bWVudC5jcmVhdGVFbGVtZW50KCJzdHlsZSIpLGUudGV4dENvbnRlbnQ9YyxhLmNvbnRlbnQuaW5zZXJ0QmVmb3JlKGUsYS5jb250ZW50LmZpcnN0Q2hpbGQpLGM9ZSk6Yz1udWxsO2EuX2dhdGhlcmVkU3R5bGU9CmN9cmV0dXJuKGE9YS5fZ2F0aGVyZWRTdHlsZSk/dGhpcy5qKGEsYik6bnVsbH07Ui5wcm90b3R5cGUuaj1mdW5jdGlvbihhLGIpe2I9dm9pZCAwPT09Yj8iIjpiO3ZhciBjPUsoYSk7dGhpcy5sKGMsYik7YS50ZXh0Q29udGVudD1KKGMpO3JldHVybiBjfTtSLnByb3RvdHlwZS5mPWZ1bmN0aW9uKGEpe3ZhciBiPXRoaXMsYz1LKGEpO0woYyxmdW5jdGlvbihhKXsiOnJvb3QiPT09YS5zZWxlY3RvciYmKGEuc2VsZWN0b3I9Imh0bWwiKTtiLmcoYSl9KTthLnRleHRDb250ZW50PUooYyk7cmV0dXJuIGN9O1IucHJvdG90eXBlLmw9ZnVuY3Rpb24oYSxiKXt2YXIgYz10aGlzO3RoaXMuYz1iO0woYSxmdW5jdGlvbihhKXtjLmcoYSl9KTt0aGlzLmM9bnVsbH07Ui5wcm90b3R5cGUuZz1mdW5jdGlvbihhKXthLmNzc1RleHQ9bmEodGhpcyxhLnBhcnNlZENzc1RleHQsYSk7Ijpyb290Ij09PWEuc2VsZWN0b3ImJihhLnNlbGVjdG9yPSI6aG9zdCA+ICoiKX07CmZ1bmN0aW9uIG5hKGEsYixjKXtiPWIucmVwbGFjZShHLGZ1bmN0aW9uKGIsZCxmLGcpe3JldHVybiBvYShhLGIsZCxmLGcsYyl9KTtyZXR1cm4gUyhhLGIsYyl9ZnVuY3Rpb24gcGEoYSxiKXtmb3IodmFyIGM9YjtjLnBhcmVudDspYz1jLnBhcmVudDt2YXIgZT17fSxkPSExO0woYyxmdW5jdGlvbihjKXsoZD1kfHxjPT09Yil8fGMuc2VsZWN0b3I9PT1iLnNlbGVjdG9yJiZPYmplY3QuYXNzaWduKGUsVChhLGMucGFyc2VkQ3NzVGV4dCkpfSk7cmV0dXJuIGV9CmZ1bmN0aW9uIFMoYSxiLGMpe2Zvcih2YXIgZTtlPUguZXhlYyhiKTspe3ZhciBkPWVbMF0sZj1lWzFdO2U9ZS5pbmRleDt2YXIgZz1iLnNsaWNlKDAsZStkLmluZGV4T2YoIkBhcHBseSIpKTtiPWIuc2xpY2UoZStkLmxlbmd0aCk7dmFyIGg9Yz9wYShhLGMpOnt9O09iamVjdC5hc3NpZ24oaCxUKGEsZykpO2Q9dm9pZCAwO3ZhciBrPWE7Zj1mLnJlcGxhY2UobGEsIiIpO3ZhciBuPVtdO3ZhciBtPWsuYS5nZXQoZik7bXx8KGsuYS5zZXQoZix7fSksbT1rLmEuZ2V0KGYpKTtpZihtKXtrLmMmJihtLmlbay5jXT0hMCk7dmFyIHE9bS5oO2ZvcihkIGluIHEpaz1oJiZoW2RdLG09W2QsIjogdmFyKCIsZiwiXy1fIixkXSxrJiZtLnB1c2goIiwiLGsucmVwbGFjZShPLCIiKSksbS5wdXNoKCIpIiksTy50ZXN0KHFbZF0pJiZtLnB1c2goIiAhaW1wb3J0YW50Iiksbi5wdXNoKG0uam9pbigiIikpfWQ9bi5qb2luKCI7ICIpO2I9ZytkK2I7SC5sYXN0SW5kZXg9ZStkLmxlbmd0aH1yZXR1cm4gYn0KZnVuY3Rpb24gVChhLGIsYyl7Yz12b2lkIDA9PT1jPyExOmM7Yj1iLnNwbGl0KCI7Iik7Zm9yKHZhciBlLGQsZj17fSxnPTAsaDtnPGIubGVuZ3RoO2crKylpZihlPWJbZ10paWYoaD1lLnNwbGl0KCI6IiksMTxoLmxlbmd0aCl7ZT1oWzBdLnRyaW0oKTtkPWguc2xpY2UoMSkuam9pbigiOiIpO2lmKGMpe3ZhciBrPWE7aD1lO3ZhciBuPW1hLmV4ZWMoZCk7biYmKG5bMV0/KGsuYnx8KGsuYj1kb2N1bWVudC5jcmVhdGVFbGVtZW50KCJtZXRhIiksay5iLnNldEF0dHJpYnV0ZSgiYXBwbHktc2hpbS1tZWFzdXJlIiwiIiksay5iLnN0eWxlLmFsbD0iaW5pdGlhbCIsZG9jdW1lbnQuaGVhZC5hcHBlbmRDaGlsZChrLmIpKSxoPXdpbmRvdy5nZXRDb21wdXRlZFN0eWxlKGsuYikuZ2V0UHJvcGVydHlWYWx1ZShoKSk6aD0iYXBwbHktc2hpbS1pbmhlcml0IixkPWgpfWZbZV09ZH1yZXR1cm4gZn1mdW5jdGlvbiBxYShhLGIpe2lmKFEpZm9yKHZhciBjIGluIGIuaSljIT09YS5jJiZRKGMpfQpmdW5jdGlvbiBvYShhLGIsYyxlLGQsZil7ZSYmTShlLGZ1bmN0aW9uKGIsYyl7YyYmYS5hLmdldChjKSYmKGQ9IkBhcHBseSAiK2MrIjsiKX0pO2lmKCFkKXJldHVybiBiO3ZhciBnPVMoYSwiIitkLGYpO2Y9Yi5zbGljZSgwLGIuaW5kZXhPZigiLS0iKSk7dmFyIGg9Zz1UKGEsZywhMCksaz1hLmEuZ2V0KGMpLG49ayYmay5oO24/aD1PYmplY3QuYXNzaWduKE9iamVjdC5jcmVhdGUobiksZyk6YS5hLnNldChjLGgpO3ZhciBtPVtdLHEsWj0hMTtmb3IocSBpbiBoKXt2YXIgRj1nW3FdO3ZvaWQgMD09PUYmJihGPSJpbml0aWFsIik7IW58fHEgaW4gbnx8KFo9ITApO20ucHVzaChjKyJfLV8iK3ErIjogIitGKX1aJiZxYShhLGspO2smJihrLmg9aCk7ZSYmKGY9YisiOyIrZik7cmV0dXJuIGYrbS5qb2luKCI7ICIpKyI7In1SLnByb3RvdHlwZS5kZXRlY3RNaXhpbj1SLnByb3RvdHlwZS5vO1IucHJvdG90eXBlLnRyYW5zZm9ybVN0eWxlPVIucHJvdG90eXBlLmo7ClIucHJvdG90eXBlLnRyYW5zZm9ybUN1c3RvbVN0eWxlPVIucHJvdG90eXBlLmY7Ui5wcm90b3R5cGUudHJhbnNmb3JtUnVsZXM9Ui5wcm90b3R5cGUubDtSLnByb3RvdHlwZS50cmFuc2Zvcm1SdWxlPVIucHJvdG90eXBlLmc7Ui5wcm90b3R5cGUudHJhbnNmb3JtVGVtcGxhdGU9Ui5wcm90b3R5cGUubTtSLnByb3RvdHlwZS5fc2VwYXJhdG9yPSJfLV8iO09iamVjdC5kZWZpbmVQcm9wZXJ0eShSLnByb3RvdHlwZSwiaW52YWxpZENhbGxiYWNrIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIFF9LHNldDpmdW5jdGlvbihhKXtRPWF9fSk7dmFyIFU9e307dmFyIHJhPVByb21pc2UucmVzb2x2ZSgpO2Z1bmN0aW9uIHNhKGEpe2lmKGE9VVthXSlhLl9hcHBseVNoaW1DdXJyZW50VmVyc2lvbj1hLl9hcHBseVNoaW1DdXJyZW50VmVyc2lvbnx8MCxhLl9hcHBseVNoaW1WYWxpZGF0aW5nVmVyc2lvbj1hLl9hcHBseVNoaW1WYWxpZGF0aW5nVmVyc2lvbnx8MCxhLl9hcHBseVNoaW1OZXh0VmVyc2lvbj0oYS5fYXBwbHlTaGltTmV4dFZlcnNpb258fDApKzF9ZnVuY3Rpb24gdGEoYSl7cmV0dXJuIGEuX2FwcGx5U2hpbUN1cnJlbnRWZXJzaW9uPT09YS5fYXBwbHlTaGltTmV4dFZlcnNpb259ZnVuY3Rpb24gdWEoYSl7YS5fYXBwbHlTaGltVmFsaWRhdGluZ1ZlcnNpb249YS5fYXBwbHlTaGltTmV4dFZlcnNpb247YS5fdmFsaWRhdGluZ3x8KGEuX3ZhbGlkYXRpbmc9ITAscmEudGhlbihmdW5jdGlvbigpe2EuX2FwcGx5U2hpbUN1cnJlbnRWZXJzaW9uPWEuX2FwcGx5U2hpbU5leHRWZXJzaW9uO2EuX3ZhbGlkYXRpbmc9ITF9KSl9O3ZhciBWPW5ldyBSO2Z1bmN0aW9uIFcoKXt0aGlzLmE9bnVsbDtWLmludmFsaWRDYWxsYmFjaz1zYX1mdW5jdGlvbiBYKGEpeyFhLmEmJndpbmRvdy5TaGFkeUNTUy5DdXN0b21TdHlsZUludGVyZmFjZSYmKGEuYT13aW5kb3cuU2hhZHlDU1MuQ3VzdG9tU3R5bGVJbnRlcmZhY2UsYS5hLnRyYW5zZm9ybUNhbGxiYWNrPWZ1bmN0aW9uKGEpe1YuZihhKX0sYS5hLnZhbGlkYXRlQ2FsbGJhY2s9ZnVuY3Rpb24oKXtyZXF1ZXN0QW5pbWF0aW9uRnJhbWUoZnVuY3Rpb24oKXthLmEuZW5xdWV1ZWQmJmEuZmx1c2hDdXN0b21TdHlsZXMoKX0pfSl9Vy5wcm90b3R5cGUucHJlcGFyZVRlbXBsYXRlPWZ1bmN0aW9uKGEsYil7WCh0aGlzKTsiIj09PU4oYSkmJihVW2JdPWEsYj1WLm0oYSxiKSxhLl9zdHlsZUFzdD1iKX07ClcucHJvdG90eXBlLmZsdXNoQ3VzdG9tU3R5bGVzPWZ1bmN0aW9uKCl7WCh0aGlzKTtpZih0aGlzLmEpe3ZhciBhPXRoaXMuYS5wcm9jZXNzU3R5bGVzKCk7aWYodGhpcy5hLmVucXVldWVkKXtmb3IodmFyIGI9MDtiPGEubGVuZ3RoO2IrKyl7dmFyIGM9dGhpcy5hLmdldFN0eWxlRm9yQ3VzdG9tU3R5bGUoYVtiXSk7YyYmVi5mKGMpfXRoaXMuYS5lbnF1ZXVlZD0hMX19fTsKVy5wcm90b3R5cGUuc3R5bGVTdWJ0cmVlPWZ1bmN0aW9uKGEsYil7WCh0aGlzKTtpZihiKWZvcih2YXIgYyBpbiBiKW51bGw9PT1jP2Euc3R5bGUucmVtb3ZlUHJvcGVydHkoYyk6YS5zdHlsZS5zZXRQcm9wZXJ0eShjLGJbY10pO2lmKGEuc2hhZG93Um9vdClmb3IodGhpcy5zdHlsZUVsZW1lbnQoYSksYT1hLnNoYWRvd1Jvb3QuY2hpbGRyZW58fGEuc2hhZG93Um9vdC5jaGlsZE5vZGVzLGI9MDtiPGEubGVuZ3RoO2IrKyl0aGlzLnN0eWxlU3VidHJlZShhW2JdKTtlbHNlIGZvcihhPWEuY2hpbGRyZW58fGEuY2hpbGROb2RlcyxiPTA7YjxhLmxlbmd0aDtiKyspdGhpcy5zdHlsZVN1YnRyZWUoYVtiXSl9OwpXLnByb3RvdHlwZS5zdHlsZUVsZW1lbnQ9ZnVuY3Rpb24oYSl7WCh0aGlzKTt2YXIgYj1hLmxvY2FsTmFtZSxjO2I/LTE8Yi5pbmRleE9mKCItIik/Yz1iOmM9YS5nZXRBdHRyaWJ1dGUmJmEuZ2V0QXR0cmlidXRlKCJpcyIpfHwiIjpjPWEuaXM7Yj1VW2NdO2lmKCEoYiYmIiIhPT1OKGIpfHwhYnx8dGEoYikpKXtpZih0YShiKXx8Yi5fYXBwbHlTaGltVmFsaWRhdGluZ1ZlcnNpb24hPT1iLl9hcHBseVNoaW1OZXh0VmVyc2lvbil0aGlzLnByZXBhcmVUZW1wbGF0ZShiLGMpLHVhKGIpO2lmKGE9YS5zaGFkb3dSb290KWlmKGE9YS5xdWVyeVNlbGVjdG9yKCJzdHlsZSIpKWEuX19jc3NSdWxlcz1iLl9zdHlsZUFzdCxhLnRleHRDb250ZW50PUooYi5fc3R5bGVBc3QpfX07Vy5wcm90b3R5cGUuc3R5bGVEb2N1bWVudD1mdW5jdGlvbihhKXtYKHRoaXMpO3RoaXMuc3R5bGVTdWJ0cmVlKGRvY3VtZW50LmJvZHksYSl9OwppZighd2luZG93LlNoYWR5Q1NTfHwhd2luZG93LlNoYWR5Q1NTLlNjb3BpbmdTaGltKXt2YXIgWT1uZXcgVyx2YT13aW5kb3cuU2hhZHlDU1MmJndpbmRvdy5TaGFkeUNTUy5DdXN0b21TdHlsZUludGVyZmFjZTt3aW5kb3cuU2hhZHlDU1M9e3ByZXBhcmVUZW1wbGF0ZTpmdW5jdGlvbihhLGIpe1kuZmx1c2hDdXN0b21TdHlsZXMoKTtZLnByZXBhcmVUZW1wbGF0ZShhLGIpfSxwcmVwYXJlVGVtcGxhdGVTdHlsZXM6ZnVuY3Rpb24oYSxiLGMpe3dpbmRvdy5TaGFkeUNTUy5wcmVwYXJlVGVtcGxhdGUoYSxiLGMpfSxwcmVwYXJlVGVtcGxhdGVEb206ZnVuY3Rpb24oKXt9LHN0eWxlU3VidHJlZTpmdW5jdGlvbihhLGIpe1kuZmx1c2hDdXN0b21TdHlsZXMoKTtZLnN0eWxlU3VidHJlZShhLGIpfSxzdHlsZUVsZW1lbnQ6ZnVuY3Rpb24oYSl7WS5mbHVzaEN1c3RvbVN0eWxlcygpO1kuc3R5bGVFbGVtZW50KGEpfSxzdHlsZURvY3VtZW50OmZ1bmN0aW9uKGEpe1kuZmx1c2hDdXN0b21TdHlsZXMoKTsKWS5zdHlsZURvY3VtZW50KGEpfSxnZXRDb21wdXRlZFN0eWxlVmFsdWU6ZnVuY3Rpb24oYSxiKXtyZXR1cm4oYT13aW5kb3cuZ2V0Q29tcHV0ZWRTdHlsZShhKS5nZXRQcm9wZXJ0eVZhbHVlKGIpKT9hLnRyaW0oKToiIn0sZmx1c2hDdXN0b21TdHlsZXM6ZnVuY3Rpb24oKXtZLmZsdXNoQ3VzdG9tU3R5bGVzKCl9LG5hdGl2ZUNzczp1LG5hdGl2ZVNoYWRvdzpsLGNzc0J1aWxkOnYsZGlzYWJsZVJ1bnRpbWU6YWF9O3ZhJiYod2luZG93LlNoYWR5Q1NTLkN1c3RvbVN0eWxlSW50ZXJmYWNlPXZhKX13aW5kb3cuU2hhZHlDU1MuQXBwbHlTaGltPVY7fSkuY2FsbCh0aGlzKTsKCgo8L3NjcmlwdD48c2NyaXB0PgooZnVuY3Rpb24oKSB7CiAgJ3VzZSBzdHJpY3QnOwoKICBjb25zdCB1c2VyUG9seW1lciA9IHdpbmRvdy5Qb2x5bWVyOwoKICAvKioKICAgKiBAbmFtZXNwYWNlIFBvbHltZXIKICAgKiBAc3VtbWFyeSBQb2x5bWVyIGlzIGEgbGlnaHR3ZWlnaHQgbGlicmFyeSBidWlsdCBvbiB0b3Agb2YgdGhlIHdlYgogICAqICAgc3RhbmRhcmRzLWJhc2VkIFdlYiBDb21wb25lbnRzIEFQSSdzLCBhbmQgbWFrZXMgaXQgZWFzeSB0byBidWlsZCB5b3VyCiAgICogICBvd24gY3VzdG9tIEhUTUwgZWxlbWVudHMuCiAgICogQHBhcmFtIHshUG9seW1lckluaXR9IGluZm8gUHJvdG90eXBlIGZvciB0aGUgY3VzdG9tIGVsZW1lbnQuIEl0IG11c3QgY29udGFpbgogICAqICAgYW4gYGlzYCBwcm9wZXJ0eSB0byBzcGVjaWZ5IHRoZSBlbGVtZW50IG5hbWUuIE90aGVyIHByb3BlcnRpZXMgcG9wdWxhdGUKICAgKiAgIHRoZSBlbGVtZW50IHByb3RvdHlwZS4gVGhlIGBwcm9wZXJ0aWVzYCwgYG9ic2VydmVyc2AsIGBob3N0QXR0cmlidXRlc2AsCiAgICogICBhbmQgYGxpc3RlbmVyc2AgcHJvcGVydGllcyBhcmUgcHJvY2Vzc2VkIHRvIGNyZWF0ZSBlbGVtZW50IGZlYXR1cmVzLgogICAqIEByZXR1cm4geyFPYmplY3R9IFJldHVybnMgYSBjdXN0b20gZWxlbWVudCBjbGFzcyBmb3IgdGhlIGdpdmVuIHByb3ZpZGVkCiAgICogICBwcm90b3R5cGUgYGluZm9gIG9iamVjdC4gVGhlIG5hbWUgb2YgdGhlIGVsZW1lbnQgaWYgZ2l2ZW4gYnkgYGluZm8uaXNgLgogICAqLwogIHdpbmRvdy5Qb2x5bWVyID0gZnVuY3Rpb24oaW5mbykgewogICAgcmV0dXJuIHdpbmRvdy5Qb2x5bWVyLl9wb2x5bWVyRm4oaW5mbyk7CiAgfTsKCiAgLy8gc3VwcG9ydCB1c2VyIHNldHRpbmdzIG9uIHRoZSBQb2x5bWVyIG9iamVjdAogIGlmICh1c2VyUG9seW1lcikgewogICAgT2JqZWN0LmFzc2lnbihQb2x5bWVyLCB1c2VyUG9seW1lcik7CiAgfQoKICAvLyBUbyBiZSBwbHVnZ2VkIGJ5IGxlZ2FjeSBpbXBsZW1lbnRhdGlvbiBpZiBsb2FkZWQKICAvKiBlc2xpbnQtZGlzYWJsZSB2YWxpZC1qc2RvYyAqLwogIC8qKgogICAqIEBwYXJhbSB7IVBvbHltZXJJbml0fSBpbmZvIFByb3RvdHlwZSBmb3IgdGhlIGN1c3RvbSBlbGVtZW50LiBJdCBtdXN0IGNvbnRhaW4KICAgKiAgIGFuIGBpc2AgcHJvcGVydHkgdG8gc3BlY2lmeSB0aGUgZWxlbWVudCBuYW1lLiBPdGhlciBwcm9wZXJ0aWVzIHBvcHVsYXRlCiAgICogICB0aGUgZWxlbWVudCBwcm90b3R5cGUuIFRoZSBgcHJvcGVydGllc2AsIGBvYnNlcnZlcnNgLCBgaG9zdEF0dHJpYnV0ZXNgLAogICAqICAgYW5kIGBsaXN0ZW5lcnNgIHByb3BlcnRpZXMgYXJlIHByb2Nlc3NlZCB0byBjcmVhdGUgZWxlbWVudCBmZWF0dXJlcy4KICAgKiBAcmV0dXJuIHshT2JqZWN0fSBSZXR1cm5zIGEgY3VzdG9tIGVsZW1lbnQgY2xhc3MgZm9yIHRoZSBnaXZlbiBwcm92aWRlZAogICAqICAgcHJvdG90eXBlIGBpbmZvYCBvYmplY3QuIFRoZSBuYW1lIG9mIHRoZSBlbGVtZW50IGlmIGdpdmVuIGJ5IGBpbmZvLmlzYC4KICAgKi8KICB3aW5kb3cuUG9seW1lci5fcG9seW1lckZuID0gZnVuY3Rpb24oaW5mbykgeyAvLyBlc2xpbnQtZGlzYWJsZS1saW5lIG5vLXVudXNlZC12YXJzCiAgICB0aHJvdyBuZXcgRXJyb3IoJ0xvYWQgcG9seW1lci5odG1sIHRvIHVzZSB0aGUgUG9seW1lcigpIGZ1bmN0aW9uLicpOwogIH07CiAgLyogZXNsaW50LWVuYWJsZSAqLwoKICB3aW5kb3cuUG9seW1lci52ZXJzaW9uID0gJzIuNy4wJzsKCiAgLyogZXNsaW50LWRpc2FibGUgbm8tdW51c2VkLXZhcnMgKi8KICAvKgogIFdoZW4gdXNpbmcgQ2xvc3VyZSBDb21waWxlciwgSlNDb21waWxlcl9yZW5hbWVQcm9wZXJ0eShwcm9wZXJ0eSwgb2JqZWN0KSBpcyByZXBsYWNlZCBieSB0aGUgbXVuZ2VkIG5hbWUgZm9yIG9iamVjdFtwcm9wZXJ0eV0KICBXZSBjYW5ub3QgYWxpYXMgdGhpcyBmdW5jdGlvbiwgc28gd2UgaGF2ZSB0byB1c2UgYSBzbWFsbCBzaGltIHRoYXQgaGFzIHRoZSBzYW1lIGJlaGF2aW9yIHdoZW4gbm90IGNvbXBpbGluZy4KICAqLwogIHdpbmRvdy5KU0NvbXBpbGVyX3JlbmFtZVByb3BlcnR5ID0gZnVuY3Rpb24ocHJvcCwgb2JqKSB7CiAgICByZXR1cm4gcHJvcDsKICB9OwogIC8qIGVzbGludC1lbmFibGUgKi8KCn0pKCk7Cjwvc2NyaXB0PjxzY3JpcHQ+CgogIChmdW5jdGlvbigpIHsKICAgICd1c2Ugc3RyaWN0JzsKCiAgICBsZXQgQ1NTX1VSTF9SWCA9IC8odXJsXCgpKFteKV0qKShcKSkvZzsKICAgIGxldCBBQlNfVVJMID0gLyheXC8pfCheIyl8KF5bXHctXGRdKjopLzsKICAgIGxldCB3b3JraW5nVVJMOwogICAgbGV0IHJlc29sdmVEb2M7CiAgICAvKioKICAgICAqIFJlc29sdmVzIHRoZSBnaXZlbiBVUkwgYWdhaW5zdCB0aGUgcHJvdmlkZWQgYGJhc2VVcmknLgogICAgICogCiAgICAgKiBOb3RlIHRoYXQgdGhpcyBmdW5jdGlvbiBwZXJmb3JtcyBubyByZXNvbHV0aW9uIGZvciBVUkxzIHRoYXQgc3RhcnQKICAgICAqIHdpdGggYC9gIChhYnNvbHV0ZSBVUkxzKSBvciBgI2AgKGhhc2ggaWRlbnRpZmllcnMpLiAgRm9yIGdlbmVyYWwgcHVycG9zZQogICAgICogVVJMIHJlc29sdXRpb24sIHVzZSBgd2luZG93LlVSTGAuCiAgICAgKgogICAgICogQG1lbWJlcm9mIFBvbHltZXIuUmVzb2x2ZVVybAogICAgICogQHBhcmFtIHtzdHJpbmd9IHVybCBJbnB1dCBVUkwgdG8gcmVzb2x2ZQogICAgICogQHBhcmFtIHs/c3RyaW5nPX0gYmFzZVVSSSBCYXNlIFVSSSB0byByZXNvbHZlIHRoZSBVUkwgYWdhaW5zdAogICAgICogQHJldHVybiB7c3RyaW5nfSByZXNvbHZlZCBVUkwKICAgICAqLwogICAgZnVuY3Rpb24gcmVzb2x2ZVVybCh1cmwsIGJhc2VVUkkpIHsKICAgICAgaWYgKHVybCAmJiBBQlNfVVJMLnRlc3QodXJsKSkgewogICAgICAgIHJldHVybiB1cmw7CiAgICAgIH0KICAgICAgLy8gTGF6eSBmZWF0dXJlIGRldGVjdGlvbi4KICAgICAgaWYgKHdvcmtpbmdVUkwgPT09IHVuZGVmaW5lZCkgewogICAgICAgIHdvcmtpbmdVUkwgPSBmYWxzZTsKICAgICAgICB0cnkgewogICAgICAgICAgY29uc3QgdSA9IG5ldyBVUkwoJ2InLCAnaHR0cDovL2EnKTsKICAgICAgICAgIHUucGF0aG5hbWUgPSAnYyUyMGQnOwogICAgICAgICAgd29ya2luZ1VSTCA9ICh1LmhyZWYgPT09ICdodHRwOi8vYS9jJTIwZCcpOwogICAgICAgIH0gY2F0Y2ggKGUpIHsKICAgICAgICAgIC8vIHNpbGVudGx5IGZhaWwKICAgICAgICB9CiAgICAgIH0KICAgICAgaWYgKCFiYXNlVVJJKSB7CiAgICAgICAgYmFzZVVSSSA9IGRvY3VtZW50LmJhc2VVUkkgfHwgd2luZG93LmxvY2F0aW9uLmhyZWY7CiAgICAgIH0KICAgICAgaWYgKHdvcmtpbmdVUkwpIHsKICAgICAgICByZXR1cm4gKG5ldyBVUkwodXJsLCBiYXNlVVJJKSkuaHJlZjsKICAgICAgfQogICAgICAvLyBGYWxsYmFjayB0byBjcmVhdGluZyBhbiBhbmNob3IgaW50byBhIGRpc2Nvbm5lY3RlZCBkb2N1bWVudC4KICAgICAgaWYgKCFyZXNvbHZlRG9jKSB7CiAgICAgICAgcmVzb2x2ZURvYyA9IGRvY3VtZW50LmltcGxlbWVudGF0aW9uLmNyZWF0ZUhUTUxEb2N1bWVudCgndGVtcCcpOwogICAgICAgIHJlc29sdmVEb2MuYmFzZSA9IHJlc29sdmVEb2MuY3JlYXRlRWxlbWVudCgnYmFzZScpOwogICAgICAgIHJlc29sdmVEb2MuaGVhZC5hcHBlbmRDaGlsZChyZXNvbHZlRG9jLmJhc2UpOwogICAgICAgIHJlc29sdmVEb2MuYW5jaG9yID0gcmVzb2x2ZURvYy5jcmVhdGVFbGVtZW50KCdhJyk7CiAgICAgICAgcmVzb2x2ZURvYy5ib2R5LmFwcGVuZENoaWxkKHJlc29sdmVEb2MuYW5jaG9yKTsKICAgICAgfQogICAgICByZXNvbHZlRG9jLmJhc2UuaHJlZiA9IGJhc2VVUkk7CiAgICAgIHJlc29sdmVEb2MuYW5jaG9yLmhyZWYgPSB1cmw7CiAgICAgIHJldHVybiByZXNvbHZlRG9jLmFuY2hvci5ocmVmIHx8IHVybDsKCiAgICB9CgogICAgLyoqCiAgICAgKiBSZXNvbHZlcyBhbnkgcmVsYXRpdmUgVVJMJ3MgaW4gdGhlIGdpdmVuIENTUyB0ZXh0IGFnYWluc3QgdGhlIHByb3ZpZGVkCiAgICAgKiBgb3duZXJEb2N1bWVudGAncyBgYmFzZVVSSWAuCiAgICAgKgogICAgICogQG1lbWJlcm9mIFBvbHltZXIuUmVzb2x2ZVVybAogICAgICogQHBhcmFtIHtzdHJpbmd9IGNzc1RleHQgQ1NTIHRleHQgdG8gcHJvY2VzcwogICAgICogQHBhcmFtIHtzdHJpbmd9IGJhc2VVUkkgQmFzZSBVUkkgdG8gcmVzb2x2ZSB0aGUgVVJMIGFnYWluc3QKICAgICAqIEByZXR1cm4ge3N0cmluZ30gUHJvY2Vzc2VkIENTUyB0ZXh0IHdpdGggcmVzb2x2ZWQgVVJMJ3MKICAgICAqLwogICAgZnVuY3Rpb24gcmVzb2x2ZUNzcyhjc3NUZXh0LCBiYXNlVVJJKSB7CiAgICAgIHJldHVybiBjc3NUZXh0LnJlcGxhY2UoQ1NTX1VSTF9SWCwgZnVuY3Rpb24obSwgcHJlLCB1cmwsIHBvc3QpIHsKICAgICAgICByZXR1cm4gcHJlICsgJ1wnJyArCiAgICAgICAgICByZXNvbHZlVXJsKHVybC5yZXBsYWNlKC9bIiddL2csICcnKSwgYmFzZVVSSSkgKwogICAgICAgICAgJ1wnJyArIHBvc3Q7CiAgICAgIH0pOwogICAgfQoKICAgIC8qKgogICAgICogUmV0dXJucyBhIHBhdGggZnJvbSBhIGdpdmVuIGB1cmxgLiBUaGUgcGF0aCBpbmNsdWRlcyB0aGUgdHJhaWxpbmcKICAgICAqIGAvYCBmcm9tIHRoZSB1cmwuCiAgICAgKgogICAgICogQG1lbWJlcm9mIFBvbHltZXIuUmVzb2x2ZVVybAogICAgICogQHBhcmFtIHtzdHJpbmd9IHVybCBJbnB1dCBVUkwgdG8gdHJhbnNmb3JtCiAgICAgKiBAcmV0dXJuIHtzdHJpbmd9IHJlc29sdmVkIHBhdGgKICAgICAqLwogICAgZnVuY3Rpb24gcGF0aEZyb21VcmwodXJsKSB7CiAgICAgIHJldHVybiB1cmwuc3Vic3RyaW5nKDAsIHVybC5sYXN0SW5kZXhPZignLycpICsgMSk7CiAgICB9CgogICAgLyoqCiAgICAgKiBNb2R1bGUgd2l0aCB1dGlsaXRpZXMgZm9yIHJlc29sdmluZyByZWxhdGl2ZSBVUkwncy4KICAgICAqCiAgICAgKiBAbmFtZXNwYWNlCiAgICAgKiBAbWVtYmVyb2YgUG9seW1lcgogICAgICogQHN1bW1hcnkgTW9kdWxlIHdpdGggdXRpbGl0aWVzIGZvciByZXNvbHZpbmcgcmVsYXRpdmUgVVJMJ3MuCiAgICAgKi8KICAgIFBvbHltZXIuUmVzb2x2ZVVybCA9IHsKICAgICAgcmVzb2x2ZUNzczogcmVzb2x2ZUNzcywKICAgICAgcmVzb2x2ZVVybDogcmVzb2x2ZVVybCwKICAgICAgcGF0aEZyb21Vcmw6IHBhdGhGcm9tVXJsCiAgICB9OwoKICB9KSgpOwoKPC9zY3JpcHQ+PHNjcmlwdD4KLyoqIEBzdXBwcmVzcyB7ZGVwcmVjYXRlZH0gKi8KKGZ1bmN0aW9uKCkgewogICd1c2Ugc3RyaWN0JzsKCiAgLyoqCiAgICogU2V0cyB0aGUgZ2xvYmFsLCBsZWdhY3kgc2V0dGluZ3MuCiAgICoKICAgKiBAZGVwcmVjYXRlZAogICAqIEBuYW1lc3BhY2UKICAgKiBAbWVtYmVyb2YgUG9seW1lcgogICAqLwogIFBvbHltZXIuU2V0dGluZ3MgPSBQb2x5bWVyLlNldHRpbmdzIHx8IHt9OwoKICBQb2x5bWVyLlNldHRpbmdzLnVzZVNoYWRvdyA9ICEod2luZG93LlNoYWR5RE9NKTsKICBQb2x5bWVyLlNldHRpbmdzLnVzZU5hdGl2ZUNTU1Byb3BlcnRpZXMgPQogICAgQm9vbGVhbighd2luZG93LlNoYWR5Q1NTIHx8IHdpbmRvdy5TaGFkeUNTUy5uYXRpdmVDc3MpOwogIFBvbHltZXIuU2V0dGluZ3MudXNlTmF0aXZlQ3VzdG9tRWxlbWVudHMgPQogICAgISh3aW5kb3cuY3VzdG9tRWxlbWVudHMucG9seWZpbGxXcmFwRmx1c2hDYWxsYmFjayk7CgoKICAvKioKICAgKiBHbG9iYWxseSBzZXR0YWJsZSBwcm9wZXJ0eSB0aGF0IGlzIGF1dG9tYXRpY2FsbHkgYXNzaWduZWQgdG8KICAgKiBgUG9seW1lci5FbGVtZW50TWl4aW5gIGluc3RhbmNlcywgdXNlZnVsIGZvciBiaW5kaW5nIGluIHRlbXBsYXRlcyB0bwogICAqIG1ha2UgVVJMJ3MgcmVsYXRpdmUgdG8gYW4gYXBwbGljYXRpb24ncyByb290LiAgRGVmYXVsdHMgdG8gdGhlIG1haW4KICAgKiBkb2N1bWVudCBVUkwsIGJ1dCBjYW4gYmUgb3ZlcnJpZGRlbiBieSB1c2Vycy4gIEl0IG1heSBiZSB1c2VmdWwgdG8gc2V0CiAgICogYFBvbHltZXIucm9vdFBhdGhgIHRvIHByb3ZpZGUgYSBzdGFibGUgYXBwbGljYXRpb24gbW91bnQgcGF0aCB3aGVuCiAgICogdXNpbmcgY2xpZW50IHNpZGUgcm91dGluZy4KICAgKgogICAqIEBtZW1iZXJvZiBQb2x5bWVyCiAgICovCiAgUG9seW1lci5yb290UGF0aCA9IFBvbHltZXIucm9vdFBhdGggfHwKICAgIFBvbHltZXIuUmVzb2x2ZVVybC5wYXRoRnJvbVVybChkb2N1bWVudC5iYXNlVVJJIHx8IHdpbmRvdy5sb2NhdGlvbi5ocmVmKTsKCiAgLyoqCiAgICogU2V0cyB0aGUgZ2xvYmFsIHJvb3RQYXRoIHByb3BlcnR5IHVzZWQgYnkgYFBvbHltZXIuRWxlbWVudE1peGluYCBhbmQKICAgKiBhdmFpbGFibGUgdmlhIGBQb2x5bWVyLnJvb3RQYXRoYC4KICAgKgogICAqIEBtZW1iZXJvZiBQb2x5bWVyCiAgICogQHBhcmFtIHtzdHJpbmd9IHBhdGggVGhlIG5ldyByb290IHBhdGgKICAgKiBAcmV0dXJuIHt2b2lkfQogICAqLwogIFBvbHltZXIuc2V0Um9vdFBhdGggPSBmdW5jdGlvbihwYXRoKSB7CiAgICBQb2x5bWVyLnJvb3RQYXRoID0gcGF0aDsKICB9OwoKICAvKioKICAgKiBBIGdsb2JhbCBjYWxsYmFjayB1c2VkIHRvIHNhbml0aXplIGFueSB2YWx1ZSBiZWZvcmUgaW5zZXJ0aW5nIGl0IGludG8gdGhlIERPTS4gVGhlIGNhbGxiYWNrIHNpZ25hdHVyZSBpczoKICAgKgogICAqICAgICBQb2x5bWVyID0gewogICAqICAgICAgIHNhbml0aXplRE9NVmFsdWU6IGZ1bmN0aW9uKHZhbHVlLCBuYW1lLCB0eXBlLCBub2RlKSB7IC4uLiB9CiAgICogICAgIH0KICAgKgogICAqIFdoZXJlOgogICAqCiAgICogYHZhbHVlYCBpcyB0aGUgdmFsdWUgdG8gc2FuaXRpemUuCiAgICogYG5hbWVgIGlzIHRoZSBuYW1lIG9mIGFuIGF0dHJpYnV0ZSBvciBwcm9wZXJ0eSAoZm9yIGV4YW1wbGUsIGhyZWYpLgogICAqIGB0eXBlYCBpbmRpY2F0ZXMgd2hlcmUgdGhlIHZhbHVlIGlzIGJlaW5nIGluc2VydGVkOiBvbmUgb2YgcHJvcGVydHksIGF0dHJpYnV0ZSwgb3IgdGV4dC4KICAgKiBgbm9kZWAgaXMgdGhlIG5vZGUgd2hlcmUgdGhlIHZhbHVlIGlzIGJlaW5nIGluc2VydGVkLgogICAqCiAgICogQHR5cGUgeyhmdW5jdGlvbigqLHN0cmluZyxzdHJpbmcsTm9kZSk6Kil8dW5kZWZpbmVkfQogICAqIEBtZW1iZXJvZiBQb2x5bWVyCiAgICovCiAgUG9seW1lci5zYW5pdGl6ZURPTVZhbHVlID0gUG9seW1lci5zYW5pdGl6ZURPTVZhbHVlIHx8IG51bGw7CgogIC8qKgogICAqIFNldHMgdGhlIGdsb2JhbCBzYW5pdGl6ZURPTVZhbHVlIGF2YWlsYWJsZSB2aWEgYFBvbHltZXIuc2FuaXRpemVET01WYWx1ZWAuCiAgICoKICAgKiBAbWVtYmVyb2YgUG9seW1lcgogICAqIEBwYXJhbSB7KGZ1bmN0aW9uKCosc3RyaW5nLHN0cmluZyxOb2RlKToqKXx1bmRlZmluZWR9IG5ld1Nhbml0aXplRE9NVmFsdWUgdGhlIGdsb2JhbCBzYW5pdGl6ZURPTVZhbHVlIGNhbGxiYWNrCiAgICogQHJldHVybiB7dm9pZH0KICAgKi8KICBQb2x5bWVyLnNldFNhbml0aXplRE9NVmFsdWUgPSBmdW5jdGlvbihuZXdTYW5pdGl6ZURPTVZhbHVlKSB7CiAgICBQb2x5bWVyLnNhbml0aXplRE9NVmFsdWUgPSBuZXdTYW5pdGl6ZURPTVZhbHVlOwogIH07CgogIC8qKgogICAqIEdsb2JhbGx5IHNldHRhYmxlIHByb3BlcnR5IHRvIG1ha2UgUG9seW1lciBHZXN0dXJlcyB1c2UgcGFzc2l2ZSBUb3VjaEV2ZW50IGxpc3RlbmVycyB3aGVuIHJlY29nbml6aW5nIGdlc3R1cmVzLgogICAqIFdoZW4gc2V0IHRvIGB0cnVlYCwgZ2VzdHVyZXMgbWFkZSBmcm9tIHRvdWNoIHdpbGwgbm90IGJlIGFibGUgdG8gcHJldmVudCBzY3JvbGxpbmcsIGFsbG93aW5nIGZvciBzbW9vdGhlcgogICAqIHNjcm9sbGluZyBwZXJmb3JtYW5jZS4KICAgKiBEZWZhdWx0cyB0byBgZmFsc2VgIGZvciBiYWNrd2FyZHMgY29tcGF0aWJpbGl0eS4KICAgKgogICAqIEBtZW1iZXJvZiBQb2x5bWVyCiAgICovCiAgUG9seW1lci5wYXNzaXZlVG91Y2hHZXN0dXJlcyA9IFBvbHltZXIucGFzc2l2ZVRvdWNoR2VzdHVyZXMgfHwgZmFsc2U7CgogIC8qKgogICAqIFNldHMgYHBhc3NpdmVUb3VjaEdlc3R1cmVzYCBnbG9iYWxseSBmb3IgYWxsIGVsZW1lbnRzIHVzaW5nIFBvbHltZXIgR2VzdHVyZXMuCiAgICoKICAgKiBAbWVtYmVyb2YgUG9seW1lcgogICAqIEBwYXJhbSB7Ym9vbGVhbn0gdXNlUGFzc2l2ZSBlbmFibGUgb3IgZGlzYWJsZSBwYXNzaXZlIHRvdWNoIGdlc3R1cmVzIGdsb2JhbGx5CiAgICogQHJldHVybiB7dm9pZH0KICAgKi8KICBQb2x5bWVyLnNldFBhc3NpdmVUb3VjaEdlc3R1cmVzID0gZnVuY3Rpb24odXNlUGFzc2l2ZSkgewogICAgUG9seW1lci5wYXNzaXZlVG91Y2hHZXN0dXJlcyA9IHVzZVBhc3NpdmU7CiAgfTsKCiAgUG9seW1lci5sZWdhY3lPcHRpbWl6YXRpb25zID0gUG9seW1lci5sZWdhY3lPcHRpbWl6YXRpb25zIHx8CiAgICAgIHdpbmRvdy5Qb2x5bWVyU2V0dGluZ3MgJiYgd2luZG93LlBvbHltZXJTZXR0aW5ncy5sZWdhY3lPcHRpbWl6YXRpb25zIHx8IGZhbHNlOwoKICAvKioKICAgKiBTZXRzIGBsZWdhY3lPcHRpbWl6YXRpb25zYCBnbG9iYWxseSBmb3IgYWxsIGVsZW1lbnRzLiBFbmFibGVzCiAgICogb3B0aW1pemF0aW9ucyB3aGVuIG9ubHkgbGVnYWN5IFBvbHltZXIoKSBzdHlsZSBlbGVtZW50cyBhcmUgdXNlZC4KICAgKgogICAqIEBtZW1iZXJvZiBQb2x5bWVyCiAgICogQHBhcmFtIHtib29sZWFufSB1c2VMZWdhY3lPcHRpbWl6YXRpb25zIGVuYWJsZSBvciBkaXNhYmxlIGxlZ2FjeSBvcHRpbWl6YXRpb25zIGdsb2JhbGx5LgogICAqIEByZXR1cm4ge3ZvaWR9CiAgICovCiAgUG9seW1lci5zZXRMZWdhY3lPcHRpbWl6YXRpb25zID0gZnVuY3Rpb24odXNlTGVnYWN5T3B0aW1pemF0aW9ucykgewogICAgUG9seW1lci5sZWdhY3lPcHRpbWl6YXRpb25zID0gdXNlTGVnYWN5T3B0aW1pemF0aW9uczsKICB9Owp9KSgpOwo8L3NjcmlwdD48c2NyaXB0PgoKKGZ1bmN0aW9uKCkgewoKICAndXNlIHN0cmljdCc7CgogIC8vIHVuaXF1ZSBnbG9iYWwgaWQgZm9yIGRlZHVwaW5nIG1peGlucy4KICBsZXQgZGVkdXBlSWQgPSAwOwoKICAvKioKICAgKiBAY29uc3RydWN0b3IKICAgKiBAZXh0ZW5kcyB7RnVuY3Rpb259CiAgICogQHByaXZhdGUKICAgKi8KICBmdW5jdGlvbiBNaXhpbkZ1bmN0aW9uKCl7fQogIC8qKiBAdHlwZSB7KFdlYWtNYXAgfCB1bmRlZmluZWQpfSAqLwogIE1peGluRnVuY3Rpb24ucHJvdG90eXBlLl9fbWl4aW5BcHBsaWNhdGlvbnM7CiAgLyoqIEB0eXBlIHsoT2JqZWN0IHwgdW5kZWZpbmVkKX0gKi8KICBNaXhpbkZ1bmN0aW9uLnByb3RvdHlwZS5fX21peGluU2V0OwoKICAvKiBlc2xpbnQtZGlzYWJsZSB2YWxpZC1qc2RvYyAqLwogIC8qKgogICAqIFdyYXBzIGFuIEVTNiBjbGFzcyBleHByZXNzaW9uIG1peGluIHN1Y2ggdGhhdCB0aGUgbWl4aW4gaXMgb25seSBhcHBsaWVkCiAgICogaWYgaXQgaGFzIG5vdCBhbHJlYWR5IGJlZW4gYXBwbGllZCBpdHMgYmFzZSBhcmd1bWVudC4gQWxzbyBtZW1vaXplcyBtaXhpbgogICAqIGFwcGxpY2F0aW9ucy4KICAgKgogICAqIEBtZW1iZXJvZiBQb2x5bWVyCiAgICogQHRlbXBsYXRlIFQKICAgKiBAcGFyYW0ge1R9IG1peGluIEVTNiBjbGFzcyBleHByZXNzaW9uIG1peGluIHRvIHdyYXAKICAgKiBAcmV0dXJuIHtUfQogICAqIEBzdXBwcmVzcyB7aW52YWxpZENhc3RzfQogICAqLwogIFBvbHltZXIuZGVkdXBpbmdNaXhpbiA9IGZ1bmN0aW9uKG1peGluKSB7CiAgICBsZXQgbWl4aW5BcHBsaWNhdGlvbnMgPSAvKiogQHR5cGUgeyFNaXhpbkZ1bmN0aW9ufSAqLyhtaXhpbikuX19taXhpbkFwcGxpY2F0aW9uczsKICAgIGlmICghbWl4aW5BcHBsaWNhdGlvbnMpIHsKICAgICAgbWl4aW5BcHBsaWNhdGlvbnMgPSBuZXcgV2Vha01hcCgpOwogICAgICAvKiogQHR5cGUgeyFNaXhpbkZ1bmN0aW9ufSAqLyhtaXhpbikuX19taXhpbkFwcGxpY2F0aW9ucyA9IG1peGluQXBwbGljYXRpb25zOwogICAgfQogICAgLy8gbWFpbnRhaW4gYSB1bmlxdWUgaWQgZm9yIGVhY2ggbWl4aW4KICAgIGxldCBtaXhpbkRlZHVwZUlkID0gZGVkdXBlSWQrKzsKICAgIGZ1bmN0aW9uIGRlZHVwaW5nTWl4aW4oYmFzZSkgewogICAgICBsZXQgYmFzZVNldCA9IC8qKiBAdHlwZSB7IU1peGluRnVuY3Rpb259ICovKGJhc2UpLl9fbWl4aW5TZXQ7CiAgICAgIGlmIChiYXNlU2V0ICYmIGJhc2VTZXRbbWl4aW5EZWR1cGVJZF0pIHsKICAgICAgICByZXR1cm4gYmFzZTsKICAgICAgfQogICAgICBsZXQgbWFwID0gbWl4aW5BcHBsaWNhdGlvbnM7CiAgICAgIGxldCBleHRlbmRlZCA9IG1hcC5nZXQoYmFzZSk7CiAgICAgIGlmICghZXh0ZW5kZWQpIHsKICAgICAgICBleHRlbmRlZCA9IC8qKiBAdHlwZSB7IUZ1bmN0aW9ufSAqLyhtaXhpbikoYmFzZSk7CiAgICAgICAgbWFwLnNldChiYXNlLCBleHRlbmRlZCk7CiAgICAgIH0KICAgICAgLy8gY29weSBpbmhlcml0ZWQgbWl4aW4gc2V0IGZyb20gdGhlIGV4dGVuZGVkIGNsYXNzLCBvciB0aGUgYmFzZSBjbGFzcwogICAgICAvLyBOT1RFOiB3ZSBhdm9pZCB1c2Ugb2YgU2V0IGhlcmUgYmVjYXVzZSBzb21lIGJyb3dzZXIgKElFMTEpCiAgICAgIC8vIGNhbm5vdCBleHRlbmQgYSBiYXNlIFNldCB2aWEgdGhlIGNvbnN0cnVjdG9yLgogICAgICBsZXQgbWl4aW5TZXQgPSBPYmplY3QuY3JlYXRlKC8qKiBAdHlwZSB7IU1peGluRnVuY3Rpb259ICovKGV4dGVuZGVkKS5fX21peGluU2V0IHx8IGJhc2VTZXQgfHwgbnVsbCk7CiAgICAgIG1peGluU2V0W21peGluRGVkdXBlSWRdID0gdHJ1ZTsKICAgICAgLyoqIEB0eXBlIHshTWl4aW5GdW5jdGlvbn0gKi8oZXh0ZW5kZWQpLl9fbWl4aW5TZXQgPSBtaXhpblNldDsKICAgICAgcmV0dXJuIGV4dGVuZGVkOwogICAgfQoKICAgIHJldHVybiAvKiogQHR5cGUge1R9ICovIChkZWR1cGluZ01peGluKTsKICB9OwogIC8qIGVzbGludC1lbmFibGUgdmFsaWQtanNkb2MgKi8KCn0pKCk7Cgo8L3NjcmlwdD48c2NyaXB0PgooZnVuY3Rpb24oKSB7CiAgJ3VzZSBzdHJpY3QnOwoKICBjb25zdCBNT0RVTEVfU1RZTEVfTElOS19TRUxFQ1RPUiA9ICdsaW5rW3JlbD1pbXBvcnRdW3R5cGV+PWNzc10nOwogIGNvbnN0IElOQ0xVREVfQVRUUiA9ICdpbmNsdWRlJzsKICBjb25zdCBTSEFEWV9VTlNDT1BFRF9BVFRSID0gJ3NoYWR5LXVuc2NvcGVkJzsKCiAgZnVuY3Rpb24gaW1wb3J0TW9kdWxlKG1vZHVsZUlkKSB7CiAgICBjb25zdCAvKiogUG9seW1lci5Eb21Nb2R1bGUgKi8gUG9seW1lckRvbU1vZHVsZSA9IGN1c3RvbUVsZW1lbnRzLmdldCgnZG9tLW1vZHVsZScpOwogICAgaWYgKCFQb2x5bWVyRG9tTW9kdWxlKSB7CiAgICAgIHJldHVybiBudWxsOwogICAgfQogICAgcmV0dXJuIFBvbHltZXJEb21Nb2R1bGUuaW1wb3J0KG1vZHVsZUlkKTsKICB9CgogIGZ1bmN0aW9uIHN0eWxlRm9ySW1wb3J0KGltcG9ydERvYykgewogICAgLy8gTk9URTogcG9seWZpbGwgYWZmb3JkYW5jZS4KICAgIC8vIHVuZGVyIHRoZSBIVE1MSW1wb3J0cyBwb2x5ZmlsbCwgdGhlcmUgd2lsbCBiZSBubyAnYm9keScsCiAgICAvLyBidXQgdGhlIGltcG9ydCBwc2V1ZG8tZG9jIGNhbiBiZSB1c2VkIGRpcmVjdGx5LgogICAgbGV0IGNvbnRhaW5lciA9IGltcG9ydERvYy5ib2R5ID8gaW1wb3J0RG9jLmJvZHkgOiBpbXBvcnREb2M7CiAgICBjb25zdCBpbXBvcnRDc3MgPSBQb2x5bWVyLlJlc29sdmVVcmwucmVzb2x2ZUNzcyhjb250YWluZXIudGV4dENvbnRlbnQsCiAgICAgIGltcG9ydERvYy5iYXNlVVJJKTsKICAgIGNvbnN0IHN0eWxlID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnc3R5bGUnKTsKICAgIHN0eWxlLnRleHRDb250ZW50ID0gaW1wb3J0Q3NzOwogICAgcmV0dXJuIHN0eWxlOwogIH0KCiAgLyoqIEB0eXBlZGVmIHt7YXNzZXRwYXRoOiBzdHJpbmd9fSAqLwogIGxldCB0ZW1wbGF0ZVdpdGhBc3NldFBhdGg7IC8vIGVzbGludC1kaXNhYmxlLWxpbmUgbm8tdW51c2VkLXZhcnMKCiAgLyoqCiAgICogTW9kdWxlIHdpdGggdXRpbGl0aWVzIGZvciBjb2xsZWN0aW9uIENTUyB0ZXh0IGZyb20gYDx0ZW1wbGF0ZXM+YCwgZXh0ZXJuYWwKICAgKiBzdHlsZXNoZWV0cywgYW5kIGBkb20tbW9kdWxlYHMuCiAgICoKICAgKiBAbmFtZXNwYWNlCiAgICogQG1lbWJlcm9mIFBvbHltZXIKICAgKiBAc3VtbWFyeSBNb2R1bGUgd2l0aCB1dGlsaXRpZXMgZm9yIGNvbGxlY3Rpb24gQ1NTIHRleHQgZnJvbSB2YXJpb3VzIHNvdXJjZXMuCiAgICovCiAgY29uc3QgU3R5bGVHYXRoZXIgPSB7CgogICAgLyoqCiAgICAgKiBSZXR1cm5zIGEgbGlzdCBvZiA8c3R5bGU+IGVsZW1lbnRzIGluIGEgc3BhY2Utc2VwYXJhdGVkIGxpc3Qgb2YgYGRvbS1tb2R1bGVgcy4KICAgICAqCiAgICAgKiBAbWVtYmVyb2YgUG9seW1lci5TdHlsZUdhdGhlcgogICAgICogQHBhcmFtIHtzdHJpbmd9IG1vZHVsZUlkcyBMaXN0IG9mIGRvbS1tb2R1bGUgaWQncyB3aXRoaW4gd2hpY2ggdG8KICAgICAqIHNlYXJjaCBmb3IgY3NzLgogICAgICogQHJldHVybiB7IUFycmF5PCFIVE1MU3R5bGVFbGVtZW50Pn0gQXJyYXkgb2YgY29udGFpbmVkIDxzdHlsZT4gZWxlbWVudHMKICAgICAqIEB0aGlzIHtTdHlsZUdhdGhlcn0KICAgICAqLwogICAgIHN0eWxlc0Zyb21Nb2R1bGVzKG1vZHVsZUlkcykgewogICAgICBjb25zdCBtb2R1bGVzID0gbW9kdWxlSWRzLnRyaW0oKS5zcGxpdCgvXHMrLyk7CiAgICAgIGNvbnN0IHN0eWxlcyA9IFtdOwogICAgICBmb3IgKGxldCBpPTA7IGkgPCBtb2R1bGVzLmxlbmd0aDsgaSsrKSB7CiAgICAgICAgc3R5bGVzLnB1c2goLi4udGhpcy5zdHlsZXNGcm9tTW9kdWxlKG1vZHVsZXNbaV0pKTsKICAgICAgfQogICAgICByZXR1cm4gc3R5bGVzOwogICAgfSwKCiAgICAvKioKICAgICAqIFJldHVybnMgYSBsaXN0IG9mIDxzdHlsZT4gZWxlbWVudHMgaW4gYSBnaXZlbiBgZG9tLW1vZHVsZWAuCiAgICAgKiBTdHlsZXMgaW4gYSBgZG9tLW1vZHVsZWAgY2FuIGNvbWUgZWl0aGVyIGZyb20gYDxzdHlsZT5gcyB3aXRoaW4gdGhlCiAgICAgKiBmaXJzdCBgPHRlbXBsYXRlPmAsIG9yIGVsc2UgZnJvbSBvbmUgb3IgbW9yZQogICAgICogYDxsaW5rIHJlbD0iaW1wb3J0IiB0eXBlPSJjc3MiPmAgbGlua3Mgb3V0c2lkZSB0aGUgdGVtcGxhdGUuCiAgICAgKgogICAgICogQG1lbWJlcm9mIFBvbHltZXIuU3R5bGVHYXRoZXIKICAgICAqIEBwYXJhbSB7c3RyaW5nfSBtb2R1bGVJZCBkb20tbW9kdWxlIGlkIHRvIGdhdGhlciBzdHlsZXMgZnJvbQogICAgICogQHJldHVybiB7IUFycmF5PCFIVE1MU3R5bGVFbGVtZW50Pn0gQXJyYXkgb2YgY29udGFpbmVkIHN0eWxlcy4KICAgICAqIEB0aGlzIHtTdHlsZUdhdGhlcn0KICAgICAqLwogICAgc3R5bGVzRnJvbU1vZHVsZShtb2R1bGVJZCkgewogICAgICBjb25zdCBtID0gaW1wb3J0TW9kdWxlKG1vZHVsZUlkKTsKCiAgICAgIGlmICghbSkgewogICAgICAgIGNvbnNvbGUud2FybignQ291bGQgbm90IGZpbmQgc3R5bGUgZGF0YSBpbiBtb2R1bGUgbmFtZWQnLCBtb2R1bGVJZCk7CiAgICAgICAgcmV0dXJuIFtdOwogICAgICB9CgogICAgICBpZiAobS5fc3R5bGVzID09PSB1bmRlZmluZWQpIHsKICAgICAgICBjb25zdCBzdHlsZXMgPSBbXTsKICAgICAgICAvLyBtb2R1bGUgaW1wb3J0czogPGxpbmsgcmVsPSJpbXBvcnQiIHR5cGU9ImNzcyI+CiAgICAgICAgc3R5bGVzLnB1c2goLi4udGhpcy5fc3R5bGVzRnJvbU1vZHVsZUltcG9ydHMobSkpOwogICAgICAgIC8vIGluY2x1ZGUgY3NzIGZyb20gdGhlIGZpcnN0IHRlbXBsYXRlIGluIHRoZSBtb2R1bGUKICAgICAgICBjb25zdCB0ZW1wbGF0ZSA9IG0ucXVlcnlTZWxlY3RvcigndGVtcGxhdGUnKTsKICAgICAgICBpZiAodGVtcGxhdGUpIHsKICAgICAgICAgIHN0eWxlcy5wdXNoKC4uLnRoaXMuc3R5bGVzRnJvbVRlbXBsYXRlKHRlbXBsYXRlLAogICAgICAgICAgICAvKiogQHR5cGUge3RlbXBsYXRlV2l0aEFzc2V0UGF0aH0gKi8obSkuYXNzZXRwYXRoKSk7CiAgICAgICAgfQoKICAgICAgICBtLl9zdHlsZXMgPSBzdHlsZXM7CiAgICAgIH0KCiAgICAgIHJldHVybiBtLl9zdHlsZXM7CiAgICB9LAoKICAgIC8qKgogICAgICogUmV0dXJucyB0aGUgYDxzdHlsZT5gIGVsZW1lbnRzIHdpdGhpbiBhIGdpdmVuIHRlbXBsYXRlLgogICAgICoKICAgICAqIEBtZW1iZXJvZiBQb2x5bWVyLlN0eWxlR2F0aGVyCiAgICAgKiBAcGFyYW0geyFIVE1MVGVtcGxhdGVFbGVtZW50fSB0ZW1wbGF0ZSBUZW1wbGF0ZSB0byBnYXRoZXIgc3R5bGVzIGZyb20KICAgICAqIEBwYXJhbSB7c3RyaW5nfSBiYXNlVVJJIGJhc2VVUkkgZm9yIHN0eWxlIGNvbnRlbnQKICAgICAqIEByZXR1cm4geyFBcnJheTwhSFRNTFN0eWxlRWxlbWVudD59IEFycmF5IG9mIHN0eWxlcwogICAgICogQHRoaXMge1N0eWxlR2F0aGVyfQogICAgICovCiAgICBzdHlsZXNGcm9tVGVtcGxhdGUodGVtcGxhdGUsIGJhc2VVUkkpIHsKICAgICAgaWYgKCF0ZW1wbGF0ZS5fc3R5bGVzKSB7CiAgICAgICAgY29uc3Qgc3R5bGVzID0gW107CiAgICAgICAgLy8gaWYgZWxlbWVudCBpcyBhIHRlbXBsYXRlLCBnZXQgY29udGVudCBmcm9tIGl0cyAuY29udGVudAogICAgICAgIGNvbnN0IGUkID0gdGVtcGxhdGUuY29udGVudC5xdWVyeVNlbGVjdG9yQWxsKCdzdHlsZScpOwogICAgICAgIGZvciAobGV0IGk9MDsgaSA8IGUkLmxlbmd0aDsgaSsrKSB7CiAgICAgICAgICBsZXQgZSA9IGUkW2ldOwogICAgICAgICAgLy8gc3VwcG9ydCBzdHlsZSBzaGFyaW5nIGJ5IGFsbG93aW5nIHN0eWxlcyB0byAiaW5jbHVkZSIKICAgICAgICAgIC8vIG90aGVyIGRvbS1tb2R1bGVzIHRoYXQgY29udGFpbiBzdHlsaW5nCiAgICAgICAgICBsZXQgaW5jbHVkZSA9IGUuZ2V0QXR0cmlidXRlKElOQ0xVREVfQVRUUik7CiAgICAgICAgICBpZiAoaW5jbHVkZSkgewogICAgICAgICAgICBzdHlsZXMucHVzaCguLi50aGlzLnN0eWxlc0Zyb21Nb2R1bGVzKGluY2x1ZGUpLmZpbHRlcihmdW5jdGlvbihpdGVtLCBpbmRleCwgc2VsZikgewogICAgICAgICAgICAgIHJldHVybiBzZWxmLmluZGV4T2YoaXRlbSkgPT09IGluZGV4OwogICAgICAgICAgICB9KSk7CiAgICAgICAgICB9CiAgICAgICAgICBpZiAoYmFzZVVSSSkgewogICAgICAgICAgICBlLnRleHRDb250ZW50ID0gUG9seW1lci5SZXNvbHZlVXJsLnJlc29sdmVDc3MoZS50ZXh0Q29udGVudCwgYmFzZVVSSSk7CiAgICAgICAgICB9CiAgICAgICAgICBzdHlsZXMucHVzaChlKTsKICAgICAgICB9CiAgICAgICAgdGVtcGxhdGUuX3N0eWxlcyA9IHN0eWxlczsKICAgICAgfQogICAgICByZXR1cm4gdGVtcGxhdGUuX3N0eWxlczsKICAgIH0sCgogICAgLyoqCiAgICAgKiBSZXR1cm5zIGEgbGlzdCBvZiA8c3R5bGU+IGVsZW1lbnRzICBmcm9tIHN0eWxlc2hlZXRzIGxvYWRlZCB2aWEgYDxsaW5rIHJlbD0iaW1wb3J0IiB0eXBlPSJjc3MiPmAgbGlua3Mgd2l0aGluIHRoZSBzcGVjaWZpZWQgYGRvbS1tb2R1bGVgLgogICAgICoKICAgICAqIEBtZW1iZXJvZiBQb2x5bWVyLlN0eWxlR2F0aGVyCiAgICAgKiBAcGFyYW0ge3N0cmluZ30gbW9kdWxlSWQgSWQgb2YgYGRvbS1tb2R1bGVgIHRvIGdhdGhlciBDU1MgZnJvbQogICAgICogQHJldHVybiB7IUFycmF5PCFIVE1MU3R5bGVFbGVtZW50Pn0gQXJyYXkgb2YgY29udGFpbmVkIHN0eWxlcy4KICAgICAqIEB0aGlzIHtTdHlsZUdhdGhlcn0KICAgICAqLwogICAgIHN0eWxlc0Zyb21Nb2R1bGVJbXBvcnRzKG1vZHVsZUlkKSB7CiAgICAgIGxldCBtID0gaW1wb3J0TW9kdWxlKG1vZHVsZUlkKTsKICAgICAgcmV0dXJuIG0gPyB0aGlzLl9zdHlsZXNGcm9tTW9kdWxlSW1wb3J0cyhtKSA6IFtdOwogICAgfSwKCiAgICAvKioKICAgICAqIEBtZW1iZXJvZiBQb2x5bWVyLlN0eWxlR2F0aGVyCiAgICAgKiBAdGhpcyB7U3R5bGVHYXRoZXJ9CiAgICAgKiBAcGFyYW0geyFIVE1MRWxlbWVudH0gbW9kdWxlIGRvbS1tb2R1bGUgZWxlbWVudCB0aGF0IGNvdWxkIGNvbnRhaW4gYDxsaW5rIHJlbD0iaW1wb3J0IiB0eXBlPSJjc3MiPmAgc3R5bGVzCiAgICAgKiBAcmV0dXJuIHshQXJyYXk8IUhUTUxTdHlsZUVsZW1lbnQ+fSBBcnJheSBvZiBjb250YWluZWQgc3R5bGVzCiAgICAgKi8KICAgIF9zdHlsZXNGcm9tTW9kdWxlSW1wb3J0cyhtb2R1bGUpIHsKICAgICAgY29uc3Qgc3R5bGVzID0gW107CiAgICAgIGNvbnN0IHAkID0gbW9kdWxlLnF1ZXJ5U2VsZWN0b3JBbGwoTU9EVUxFX1NUWUxFX0xJTktfU0VMRUNUT1IpOwogICAgICBmb3IgKGxldCBpPTA7IGkgPCBwJC5sZW5ndGg7IGkrKykgewogICAgICAgIGxldCBwID0gcCRbaV07CiAgICAgICAgaWYgKHAuaW1wb3J0KSB7CiAgICAgICAgICBjb25zdCBpbXBvcnREb2MgPSBwLmltcG9ydDsKICAgICAgICAgIGNvbnN0IHVuc2NvcGVkID0gcC5oYXNBdHRyaWJ1dGUoU0hBRFlfVU5TQ09QRURfQVRUUik7CiAgICAgICAgICBpZiAodW5zY29wZWQgJiYgIWltcG9ydERvYy5fdW5zY29wZWRTdHlsZSkgewogICAgICAgICAgICBjb25zdCBzdHlsZSA9IHN0eWxlRm9ySW1wb3J0KGltcG9ydERvYyk7CiAgICAgICAgICAgIHN0eWxlLnNldEF0dHJpYnV0ZShTSEFEWV9VTlNDT1BFRF9BVFRSLCAnJyk7CiAgICAgICAgICAgIGltcG9ydERvYy5fdW5zY29wZWRTdHlsZSA9IHN0eWxlOwogICAgICAgICAgfSBlbHNlIGlmICghaW1wb3J0RG9jLl9zdHlsZSkgewogICAgICAgICAgICBpbXBvcnREb2MuX3N0eWxlID0gc3R5bGVGb3JJbXBvcnQoaW1wb3J0RG9jKTsKICAgICAgICAgIH0KICAgICAgICAgIHN0eWxlcy5wdXNoKHVuc2NvcGVkID8gaW1wb3J0RG9jLl91bnNjb3BlZFN0eWxlIDogaW1wb3J0RG9jLl9zdHlsZSk7CiAgICAgICAgfQogICAgICB9CiAgICAgIHJldHVybiBzdHlsZXM7CiAgICB9LAoKICAgIC8qKgogICAgICoKICAgICAqIFJldHVybnMgQ1NTIHRleHQgb2Ygc3R5bGVzIGluIGEgc3BhY2Utc2VwYXJhdGVkIGxpc3Qgb2YgYGRvbS1tb2R1bGVgcy4KICAgICAqIE5vdGU6IFRoaXMgbWV0aG9kIGlzIGRlcHJlY2F0ZWQsIHVzZSBgc3R5bGVzRnJvbU1vZHVsZXNgIGluc3RlYWQuCiAgICAgKgogICAgICogQGRlcHJlY2F0ZWQKICAgICAqIEBtZW1iZXJvZiBQb2x5bWVyLlN0eWxlR2F0aGVyCiAgICAgKiBAcGFyYW0ge3N0cmluZ30gbW9kdWxlSWRzIExpc3Qgb2YgZG9tLW1vZHVsZSBpZCdzIHdpdGhpbiB3aGljaCB0bwogICAgICogc2VhcmNoIGZvciBjc3MuCiAgICAgKiBAcmV0dXJuIHtzdHJpbmd9IENvbmNhdGVuYXRlZCBDU1MgY29udGVudCBmcm9tIHNwZWNpZmllZCBgZG9tLW1vZHVsZWBzCiAgICAgKiBAdGhpcyB7U3R5bGVHYXRoZXJ9CiAgICAgKi8KICAgICBjc3NGcm9tTW9kdWxlcyhtb2R1bGVJZHMpIHsKICAgICAgbGV0IG1vZHVsZXMgPSBtb2R1bGVJZHMudHJpbSgpLnNwbGl0KC9ccysvKTsKICAgICAgbGV0IGNzc1RleHQgPSAnJzsKICAgICAgZm9yIChsZXQgaT0wOyBpIDwgbW9kdWxlcy5sZW5ndGg7IGkrKykgewogICAgICAgIGNzc1RleHQgKz0gdGhpcy5jc3NGcm9tTW9kdWxlKG1vZHVsZXNbaV0pOwogICAgICB9CiAgICAgIHJldHVybiBjc3NUZXh0OwogICAgfSwKCiAgICAvKioKICAgICAqIFJldHVybnMgQ1NTIHRleHQgb2Ygc3R5bGVzIGluIGEgZ2l2ZW4gYGRvbS1tb2R1bGVgLiAgQ1NTIGluIGEgYGRvbS1tb2R1bGVgCiAgICAgKiBjYW4gY29tZSBlaXRoZXIgZnJvbSBgPHN0eWxlPmBzIHdpdGhpbiB0aGUgZmlyc3QgYDx0ZW1wbGF0ZT5gLCBvciBlbHNlCiAgICAgKiBmcm9tIG9uZSBvciBtb3JlIGA8bGluayByZWw9ImltcG9ydCIgdHlwZT0iY3NzIj5gIGxpbmtzIG91dHNpZGUgdGhlCiAgICAgKiB0ZW1wbGF0ZS4KICAgICAqCiAgICAgKiBBbnkgYDxzdHlsZXM+YCBwcm9jZXNzZWQgYXJlIHJlbW92ZWQgZnJvbSB0aGVpciBvcmlnaW5hbCBsb2NhdGlvbi4KICAgICAqIE5vdGU6IFRoaXMgbWV0aG9kIGlzIGRlcHJlY2F0ZWQsIHVzZSBgc3R5bGVGcm9tTW9kdWxlYCBpbnN0ZWFkLgogICAgICoKICAgICAqIEBkZXByZWNhdGVkCiAgICAgKiBAbWVtYmVyb2YgUG9seW1lci5TdHlsZUdhdGhlcgogICAgICogQHBhcmFtIHtzdHJpbmd9IG1vZHVsZUlkIGRvbS1tb2R1bGUgaWQgdG8gZ2F0aGVyIHN0eWxlcyBmcm9tCiAgICAgKiBAcmV0dXJuIHtzdHJpbmd9IENvbmNhdGVuYXRlZCBDU1MgY29udGVudCBmcm9tIHNwZWNpZmllZCBgZG9tLW1vZHVsZWAKICAgICAqIEB0aGlzIHtTdHlsZUdhdGhlcn0KICAgICAqLwogICAgY3NzRnJvbU1vZHVsZShtb2R1bGVJZCkgewogICAgICBsZXQgbSA9IGltcG9ydE1vZHVsZShtb2R1bGVJZCk7CiAgICAgIGlmIChtICYmIG0uX2Nzc1RleHQgPT09IHVuZGVmaW5lZCkgewogICAgICAgIC8vIG1vZHVsZSBpbXBvcnRzOiA8bGluayByZWw9ImltcG9ydCIgdHlwZT0iY3NzIj4KICAgICAgICBsZXQgY3NzVGV4dCA9IHRoaXMuX2Nzc0Zyb21Nb2R1bGVJbXBvcnRzKG0pOwogICAgICAgIC8vIGluY2x1ZGUgY3NzIGZyb20gdGhlIGZpcnN0IHRlbXBsYXRlIGluIHRoZSBtb2R1bGUKICAgICAgICBsZXQgdCA9IG0ucXVlcnlTZWxlY3RvcigndGVtcGxhdGUnKTsKICAgICAgICBpZiAodCkgewogICAgICAgICAgY3NzVGV4dCArPSB0aGlzLmNzc0Zyb21UZW1wbGF0ZSh0LAogICAgICAgICAgICAvKiogQHR5cGUge3RlbXBsYXRlV2l0aEFzc2V0UGF0aH0gKi8obSkuYXNzZXRwYXRoKTsKICAgICAgICB9CiAgICAgICAgbS5fY3NzVGV4dCA9IGNzc1RleHQgfHwgbnVsbDsKICAgICAgfQogICAgICBpZiAoIW0pIHsKICAgICAgICBjb25zb2xlLndhcm4oJ0NvdWxkIG5vdCBmaW5kIHN0eWxlIGRhdGEgaW4gbW9kdWxlIG5hbWVkJywgbW9kdWxlSWQpOwogICAgICB9CiAgICAgIHJldHVybiBtICYmIG0uX2Nzc1RleHQgfHwgJyc7CiAgICB9LAoKICAgIC8qKgogICAgICogUmV0dXJucyBDU1MgdGV4dCBvZiBgPHN0eWxlcz5gIHdpdGhpbiBhIGdpdmVuIHRlbXBsYXRlLgogICAgICoKICAgICAqIEFueSBgPHN0eWxlcz5gIHByb2Nlc3NlZCBhcmUgcmVtb3ZlZCBmcm9tIHRoZWlyIG9yaWdpbmFsIGxvY2F0aW9uLgogICAgICogTm90ZTogVGhpcyBtZXRob2QgaXMgZGVwcmVjYXRlZCwgdXNlIGBzdHlsZUZyb21UZW1wbGF0ZWAgaW5zdGVhZC4KICAgICAqCiAgICAgKiBAZGVwcmVjYXRlZAogICAgICogQG1lbWJlcm9mIFBvbHltZXIuU3R5bGVHYXRoZXIKICAgICAqIEBwYXJhbSB7IUhUTUxUZW1wbGF0ZUVsZW1lbnR9IHRlbXBsYXRlIFRlbXBsYXRlIHRvIGdhdGhlciBzdHlsZXMgZnJvbQogICAgICogQHBhcmFtIHtzdHJpbmd9IGJhc2VVUkkgQmFzZSBVUkkgdG8gcmVzb2x2ZSB0aGUgVVJMIGFnYWluc3QKICAgICAqIEByZXR1cm4ge3N0cmluZ30gQ29uY2F0ZW5hdGVkIENTUyBjb250ZW50IGZyb20gc3BlY2lmaWVkIHRlbXBsYXRlCiAgICAgKiBAdGhpcyB7U3R5bGVHYXRoZXJ9CiAgICAgKi8KICAgIGNzc0Zyb21UZW1wbGF0ZSh0ZW1wbGF0ZSwgYmFzZVVSSSkgewogICAgICBsZXQgY3NzVGV4dCA9ICcnOwogICAgICBjb25zdCBlJCA9IHRoaXMuc3R5bGVzRnJvbVRlbXBsYXRlKHRlbXBsYXRlLCBiYXNlVVJJKTsKICAgICAgLy8gaWYgZWxlbWVudCBpcyBhIHRlbXBsYXRlLCBnZXQgY29udGVudCBmcm9tIGl0cyAuY29udGVudAogICAgICBmb3IgKGxldCBpPTA7IGkgPCBlJC5sZW5ndGg7IGkrKykgewogICAgICAgIGxldCBlID0gZSRbaV07CiAgICAgICAgaWYgKGUucGFyZW50Tm9kZSkgewogICAgICAgICAgZS5wYXJlbnROb2RlLnJlbW92ZUNoaWxkKGUpOwogICAgICAgIH0KICAgICAgICBjc3NUZXh0ICs9IGUudGV4dENvbnRlbnQ7CiAgICAgIH0KICAgICAgcmV0dXJuIGNzc1RleHQ7CiAgICB9LAoKICAgIC8qKgogICAgICogUmV0dXJucyBDU1MgdGV4dCBmcm9tIHN0eWxlc2hlZXRzIGxvYWRlZCB2aWEgYDxsaW5rIHJlbD0iaW1wb3J0IiB0eXBlPSJjc3MiPmAKICAgICAqIGxpbmtzIHdpdGhpbiB0aGUgc3BlY2lmaWVkIGBkb20tbW9kdWxlYC4KICAgICAqCiAgICAgKiBOb3RlOiBUaGlzIG1ldGhvZCBpcyBkZXByZWNhdGVkLCB1c2UgYHN0eWxlc0Zyb21Nb2R1bGVJbXBvcnRzYCBpbnN0ZWFkLgogICAgICoKICAgICAqIEBkZXByZWNhdGVkCiAgICAgKgogICAgICogQG1lbWJlcm9mIFBvbHltZXIuU3R5bGVHYXRoZXIKICAgICAqIEBwYXJhbSB7c3RyaW5nfSBtb2R1bGVJZCBJZCBvZiBgZG9tLW1vZHVsZWAgdG8gZ2F0aGVyIENTUyBmcm9tCiAgICAgKiBAcmV0dXJuIHtzdHJpbmd9IENvbmNhdGVuYXRlZCBDU1MgY29udGVudCBmcm9tIGxpbmtzIGluIHNwZWNpZmllZCBgZG9tLW1vZHVsZWAKICAgICAqIEB0aGlzIHtTdHlsZUdhdGhlcn0KICAgICAqLwogICAgY3NzRnJvbU1vZHVsZUltcG9ydHMobW9kdWxlSWQpIHsKICAgICAgbGV0IG0gPSBpbXBvcnRNb2R1bGUobW9kdWxlSWQpOwogICAgICByZXR1cm4gbSA/IHRoaXMuX2Nzc0Zyb21Nb2R1bGVJbXBvcnRzKG0pIDogJyc7CiAgICB9LAoKICAgIC8qKgogICAgICogQGRlcHJlY2F0ZWQKICAgICAqIEBtZW1iZXJvZiBQb2x5bWVyLlN0eWxlR2F0aGVyCiAgICAgKiBAdGhpcyB7U3R5bGVHYXRoZXJ9CiAgICAgKiBAcGFyYW0geyFIVE1MRWxlbWVudH0gbW9kdWxlIGRvbS1tb2R1bGUgZWxlbWVudCB0aGF0IGNvdWxkIGNvbnRhaW4gYDxsaW5rIHJlbD0iaW1wb3J0IiB0eXBlPSJjc3MiPmAgc3R5bGVzCiAgICAgKiBAcmV0dXJuIHtzdHJpbmd9IENvbmNhdGVuYXRlZCBDU1MgY29udGVudCBmcm9tIGxpbmtzIGluIHRoZSBkb20tbW9kdWxlCiAgICAgKi8KICAgICBfY3NzRnJvbU1vZHVsZUltcG9ydHMobW9kdWxlKSB7CiAgICAgIGxldCBjc3NUZXh0ID0gJyc7CiAgICAgIGxldCBzdHlsZXMgPSB0aGlzLl9zdHlsZXNGcm9tTW9kdWxlSW1wb3J0cyhtb2R1bGUpOwogICAgICBmb3IgKGxldCBpPTA7IGkgPCBzdHlsZXMubGVuZ3RoOyBpKyspIHsKICAgICAgICBjc3NUZXh0ICs9IHN0eWxlc1tpXS50ZXh0Q29udGVudDsKICAgICAgfQogICAgICByZXR1cm4gY3NzVGV4dDsKICAgIH0KICB9OwoKICBQb2x5bWVyLlN0eWxlR2F0aGVyID0gU3R5bGVHYXRoZXI7Cn0pKCk7Cjwvc2NyaXB0PjxzY3JpcHQ+CihmdW5jdGlvbigpIHsKICAndXNlIHN0cmljdCc7CgogIGxldCBtb2R1bGVzID0ge307CiAgbGV0IGxjTW9kdWxlcyA9IHt9OwogIGZ1bmN0aW9uIHNldE1vZHVsZShpZCwgbW9kdWxlKSB7CiAgICAvLyBzdG9yZSBpZCBzZXBhcmF0ZSBmcm9tIGxvd2VyY2FzZWQgaWQgc28gdGhhdAogICAgLy8gaW4gYWxsIGNhc2VzIG1peGVkQ2FzZSBpZCB3aWxsIHN0b3JlZCBkaXN0aW5jdGx5CiAgICAvLyBhbmQgbG93ZXJjYXNlIHZlcnNpb24gaXMgYSBmYWxsYmFjawogICAgbW9kdWxlc1tpZF0gPSBsY01vZHVsZXNbaWQudG9Mb3dlckNhc2UoKV0gPSBtb2R1bGU7CiAgfQogIGZ1bmN0aW9uIGZpbmRNb2R1bGUoaWQpIHsKICAgIHJldHVybiBtb2R1bGVzW2lkXSB8fCBsY01vZHVsZXNbaWQudG9Mb3dlckNhc2UoKV07CiAgfQoKICBmdW5jdGlvbiBzdHlsZU91dHNpZGVUZW1wbGF0ZUNoZWNrKGluc3QpIHsKICAgIGlmIChpbnN0LnF1ZXJ5U2VsZWN0b3IoJ3N0eWxlJykpIHsKICAgICAgY29uc29sZS53YXJuKCdkb20tbW9kdWxlICVzIGhhcyBzdHlsZSBvdXRzaWRlIHRlbXBsYXRlJywgaW5zdC5pZCk7CiAgICB9CiAgfQoKICAvKioKICAgKiBUaGUgYGRvbS1tb2R1bGVgIGVsZW1lbnQgcmVnaXN0ZXJzIHRoZSBkb20gaXQgY29udGFpbnMgdG8gdGhlIG5hbWUgZ2l2ZW4KICAgKiBieSB0aGUgbW9kdWxlJ3MgaWQgYXR0cmlidXRlLiBJdCBwcm92aWRlcyBhIHVuaWZpZWQgZGF0YWJhc2Ugb2YgZG9tCiAgICogYWNjZXNzaWJsZSB2aWEgaXRzIHN0YXRpYyBgaW1wb3J0YCBBUEkuCiAgICoKICAgKiBBIGtleSB1c2UgY2FzZSBvZiBgZG9tLW1vZHVsZWAgaXMgZm9yIHByb3ZpZGluZyBjdXN0b20gZWxlbWVudCBgPHRlbXBsYXRlPmBzCiAgICogdmlhIEhUTUwgaW1wb3J0cyB0aGF0IGFyZSBwYXJzZWQgYnkgdGhlIG5hdGl2ZSBIVE1MIHBhcnNlciwgdGhhdCBjYW4gYmUKICAgKiByZWxvY2F0ZWQgZHVyaW5nIGEgYnVuZGxpbmcgcGFzcyBhbmQgc3RpbGwgbG9va2VkIHVwIGJ5IGBpZGAuCiAgICoKICAgKiBFeGFtcGxlOgogICAqCiAgICogICAgIDxkb20tbW9kdWxlIGlkPSJmb28iPgogICAqICAgICAgIDxpbWcgc3JjPSJzdHVmZi5wbmciPgogICAqICAgICA8L2RvbS1tb2R1bGU+CiAgICoKICAgKiBUaGVuIGluIGNvZGUgaW4gc29tZSBvdGhlciBsb2NhdGlvbiB0aGF0IGNhbm5vdCBhY2Nlc3MgdGhlIGRvbS1tb2R1bGUgYWJvdmUKICAgKgogICAqICAgICBsZXQgaW1nID0gY3VzdG9tRWxlbWVudHMuZ2V0KCdkb20tbW9kdWxlJykuaW1wb3J0KCdmb28nLCAnaW1nJyk7CiAgICoKICAgKiBAY3VzdG9tRWxlbWVudAogICAqIEBleHRlbmRzIEhUTUxFbGVtZW50CiAgICogQG1lbWJlcm9mIFBvbHltZXIKICAgKiBAc3VtbWFyeSBDdXN0b20gZWxlbWVudCB0aGF0IHByb3ZpZGVzIGEgcmVnaXN0cnkgb2YgcmVsb2NhdGFibGUgRE9NIGNvbnRlbnQKICAgKiAgIGJ5IGBpZGAgdGhhdCBpcyBhZ25vc3RpYyB0byBidW5kbGluZy4KICAgKiBAdW5yZXN0cmljdGVkCiAgICovCiAgY2xhc3MgRG9tTW9kdWxlIGV4dGVuZHMgSFRNTEVsZW1lbnQgewoKICAgIHN0YXRpYyBnZXQgb2JzZXJ2ZWRBdHRyaWJ1dGVzKCkgeyByZXR1cm4gWydpZCddOyB9CgogICAgLyoqCiAgICAgKiBSZXRyaWV2ZXMgdGhlIGVsZW1lbnQgc3BlY2lmaWVkIGJ5IHRoZSBjc3MgYHNlbGVjdG9yYCBpbiB0aGUgbW9kdWxlCiAgICAgKiByZWdpc3RlcmVkIGJ5IGBpZGAuIEZvciBleGFtcGxlLCB0aGlzLmltcG9ydCgnZm9vJywgJ2ltZycpOwogICAgICogQHBhcmFtIHtzdHJpbmd9IGlkIFRoZSBpZCBvZiB0aGUgZG9tLW1vZHVsZSBpbiB3aGljaCB0byBzZWFyY2guCiAgICAgKiBAcGFyYW0ge3N0cmluZz19IHNlbGVjdG9yIFRoZSBjc3Mgc2VsZWN0b3IgYnkgd2hpY2ggdG8gZmluZCB0aGUgZWxlbWVudC4KICAgICAqIEByZXR1cm4ge0VsZW1lbnR9IFJldHVybnMgdGhlIGVsZW1lbnQgd2hpY2ggbWF0Y2hlcyBgc2VsZWN0b3JgIGluIHRoZQogICAgICogbW9kdWxlIHJlZ2lzdGVyZWQgYXQgdGhlIHNwZWNpZmllZCBgaWRgLgogICAgICovCiAgICBzdGF0aWMgaW1wb3J0KGlkLCBzZWxlY3RvcikgewogICAgICBpZiAoaWQpIHsKICAgICAgICBsZXQgbSA9IGZpbmRNb2R1bGUoaWQpOwogICAgICAgIGlmIChtICYmIHNlbGVjdG9yKSB7CiAgICAgICAgICByZXR1cm4gbS5xdWVyeVNlbGVjdG9yKHNlbGVjdG9yKTsKICAgICAgICB9CiAgICAgICAgcmV0dXJuIG07CiAgICAgIH0KICAgICAgcmV0dXJuIG51bGw7CiAgICB9CgogICAgLyogZXNsaW50LWRpc2FibGUgbm8tdW51c2VkLXZhcnMgKi8KICAgIC8qKgogICAgICogQHBhcmFtIHtzdHJpbmd9IG5hbWUgTmFtZSBvZiBhdHRyaWJ1dGUuCiAgICAgKiBAcGFyYW0gez9zdHJpbmd9IG9sZCBPbGQgdmFsdWUgb2YgYXR0cmlidXRlLgogICAgICogQHBhcmFtIHs/c3RyaW5nfSB2YWx1ZSBDdXJyZW50IHZhbHVlIG9mIGF0dHJpYnV0ZS4KICAgICAqIEBwYXJhbSB7P3N0cmluZ30gbmFtZXNwYWNlIEF0dHJpYnV0ZSBuYW1lc3BhY2UuCiAgICAgKiBAcmV0dXJuIHt2b2lkfQogICAgICovCiAgICBhdHRyaWJ1dGVDaGFuZ2VkQ2FsbGJhY2sobmFtZSwgb2xkLCB2YWx1ZSwgbmFtZXNwYWNlKSB7CiAgICAgIGlmIChvbGQgIT09IHZhbHVlKSB7CiAgICAgICAgdGhpcy5yZWdpc3RlcigpOwogICAgICB9CiAgICB9CiAgICAvKiBlc2xpbnQtZW5hYmxlIG5vLXVudXNlZC1hcmdzICovCgogICAgLyoqCiAgICAgKiBUaGUgYWJzb2x1dGUgVVJMIG9mIHRoZSBvcmlnaW5hbCBsb2NhdGlvbiBvZiB0aGlzIGBkb20tbW9kdWxlYC4KICAgICAqCiAgICAgKiBUaGlzIHZhbHVlIHdpbGwgZGlmZmVyIGZyb20gdGhpcyBlbGVtZW50J3MgYG93bmVyRG9jdW1lbnRgIGluIHRoZQogICAgICogZm9sbG93aW5nIHdheXM6CiAgICAgKiAtIFRha2VzIGludG8gYWNjb3VudCBhbnkgYGFzc2V0cGF0aGAgYXR0cmlidXRlIGFkZGVkIGR1cmluZyBidW5kbGluZwogICAgICogICB0byBpbmRpY2F0ZSB0aGUgb3JpZ2luYWwgbG9jYXRpb24gcmVsYXRpdmUgdG8gdGhlIGJ1bmRsZWQgbG9jYXRpb24KICAgICAqIC0gVXNlcyB0aGUgSFRNTEltcG9ydHMgcG9seWZpbGwncyBgaW1wb3J0Rm9yRWxlbWVudGAgQVBJIHRvIGVuc3VyZQogICAgICogICB0aGUgcGF0aCBpcyByZWxhdGl2ZSB0byB0aGUgaW1wb3J0IGRvY3VtZW50J3MgbG9jYXRpb24gc2luY2UKICAgICAqICAgYG93bmVyRG9jdW1lbnRgIGlzIG5vdCBjdXJyZW50bHkgcG9seWZpbGxlZAogICAgICovCiAgICBnZXQgYXNzZXRwYXRoKCkgewogICAgICAvLyBEb24ndCBvdmVycmlkZSBleGlzdGluZyBhc3NldHBhdGguCiAgICAgIGlmICghdGhpcy5fX2Fzc2V0cGF0aCkgewogICAgICAgIC8vIG5vdGU6IGFzc2V0cGF0aCBzZXQgdmlhIGFuIGF0dHJpYnV0ZSBtdXN0IGJlIHJlbGF0aXZlIHRvIHRoaXMKICAgICAgICAvLyBlbGVtZW50J3MgbG9jYXRpb247IGFjY29tb2RhdGUgcG9seWZpbGxlZCBIVE1MSW1wb3J0cwogICAgICAgIGNvbnN0IG93bmVyID0gd2luZG93LkhUTUxJbXBvcnRzICYmIEhUTUxJbXBvcnRzLmltcG9ydEZvckVsZW1lbnQgPwogICAgICAgICAgSFRNTEltcG9ydHMuaW1wb3J0Rm9yRWxlbWVudCh0aGlzKSB8fCBkb2N1bWVudCA6IHRoaXMub3duZXJEb2N1bWVudDsKICAgICAgICBjb25zdCB1cmwgPSBQb2x5bWVyLlJlc29sdmVVcmwucmVzb2x2ZVVybCgKICAgICAgICAgIHRoaXMuZ2V0QXR0cmlidXRlKCdhc3NldHBhdGgnKSB8fCAnJywgb3duZXIuYmFzZVVSSSk7CiAgICAgICAgdGhpcy5fX2Fzc2V0cGF0aCA9IFBvbHltZXIuUmVzb2x2ZVVybC5wYXRoRnJvbVVybCh1cmwpOwogICAgICB9CiAgICAgIHJldHVybiB0aGlzLl9fYXNzZXRwYXRoOwogICAgfQoKICAgIC8qKgogICAgICogUmVnaXN0ZXJzIHRoZSBkb20tbW9kdWxlIGF0IGEgZ2l2ZW4gaWQuIFRoaXMgbWV0aG9kIHNob3VsZCBvbmx5IGJlIGNhbGxlZAogICAgICogd2hlbiBhIGRvbS1tb2R1bGUgaXMgaW1wZXJhdGl2ZWx5IGNyZWF0ZWQuIEZvcgogICAgICogZXhhbXBsZSwgYGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RvbS1tb2R1bGUnKS5yZWdpc3RlcignZm9vJylgLgogICAgICogQHBhcmFtIHtzdHJpbmc9fSBpZCBUaGUgaWQgYXQgd2hpY2ggdG8gcmVnaXN0ZXIgdGhlIGRvbS1tb2R1bGUuCiAgICAgKiBAcmV0dXJuIHt2b2lkfQogICAgICovCiAgICByZWdpc3RlcihpZCkgewogICAgICBpZCA9IGlkIHx8IHRoaXMuaWQ7CiAgICAgIGlmIChpZCkgewogICAgICAgIC8vIFVuZGVyIHN0cmljdFRlbXBsYXRlUG9saWN5LCByZWplY3QgYW5kIG51bGwgb3V0IGFueSByZS1yZWdpc3RlcmVkCiAgICAgICAgLy8gZG9tLW1vZHVsZSBzaW5jZSBpdCBpcyBhbWJpZ3VvdXMgd2hldGhlciBmaXJzdC1pbiBvciBsYXN0LWluIGlzIHRydXN0ZWQgCiAgICAgICAgaWYgKFBvbHltZXIuc3RyaWN0VGVtcGxhdGVQb2xpY3kgJiYgZmluZE1vZHVsZShpZCkgIT09IHVuZGVmaW5lZCkgewogICAgICAgICAgc2V0TW9kdWxlKGlkLCBudWxsKTsKICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgc3RyaWN0VGVtcGxhdGVQb2xpY3k6IGRvbS1tb2R1bGUgJHtpZH0gcmUtcmVnaXN0ZXJlZGApOwogICAgICAgIH0KICAgICAgICB0aGlzLmlkID0gaWQ7CiAgICAgICAgc2V0TW9kdWxlKGlkLCB0aGlzKTsKICAgICAgICBzdHlsZU91dHNpZGVUZW1wbGF0ZUNoZWNrKHRoaXMpOwogICAgICB9CiAgICB9CiAgfQoKICBEb21Nb2R1bGUucHJvdG90eXBlWydtb2R1bGVzJ10gPSBtb2R1bGVzOwoKICBjdXN0b21FbGVtZW50cy5kZWZpbmUoJ2RvbS1tb2R1bGUnLCBEb21Nb2R1bGUpOwoKICAvKiogQGNvbnN0ICovCiAgUG9seW1lci5Eb21Nb2R1bGUgPSBEb21Nb2R1bGU7Cgp9KSgpOwo8L3NjcmlwdD48c2NyaXB0PgooZnVuY3Rpb24oKSB7CiAgJ3VzZSBzdHJpY3QnOwoKICAvKioKICAgKiBNb2R1bGUgd2l0aCB1dGlsaXRpZXMgZm9yIG1hbmlwdWxhdGluZyBzdHJ1Y3R1cmVkIGRhdGEgcGF0aCBzdHJpbmdzLgogICAqCiAgICogQG5hbWVzcGFjZQogICAqIEBtZW1iZXJvZiBQb2x5bWVyCiAgICogQHN1bW1hcnkgTW9kdWxlIHdpdGggdXRpbGl0aWVzIGZvciBtYW5pcHVsYXRpbmcgc3RydWN0dXJlZCBkYXRhIHBhdGggc3RyaW5ncy4KICAgKi8KICBjb25zdCBQYXRoID0gewoKICAgIC8qKgogICAgICogUmV0dXJucyB0cnVlIGlmIHRoZSBnaXZlbiBzdHJpbmcgaXMgYSBzdHJ1Y3R1cmVkIGRhdGEgcGF0aCAoaGFzIGRvdHMpLgogICAgICoKICAgICAqIEV4YW1wbGU6CiAgICAgKgogICAgICogYGBgCiAgICAgKiBQb2x5bWVyLlBhdGguaXNQYXRoKCdmb28uYmFyLmJheicpIC8vIHRydWUKICAgICAqIFBvbHltZXIuUGF0aC5pc1BhdGgoJ2ZvbycpICAgICAgICAgLy8gZmFsc2UKICAgICAqIGBgYAogICAgICoKICAgICAqIEBtZW1iZXJvZiBQb2x5bWVyLlBhdGgKICAgICAqIEBwYXJhbSB7c3RyaW5nfSBwYXRoIFBhdGggc3RyaW5nCiAgICAgKiBAcmV0dXJuIHtib29sZWFufSBUcnVlIGlmIHRoZSBzdHJpbmcgY29udGFpbmVkIG9uZSBvciBtb3JlIGRvdHMKICAgICAqLwogICAgaXNQYXRoOiBmdW5jdGlvbihwYXRoKSB7CiAgICAgIHJldHVybiBwYXRoLmluZGV4T2YoJy4nKSA+PSAwOwogICAgfSwKCiAgICAvKioKICAgICAqIFJldHVybnMgdGhlIHJvb3QgcHJvcGVydHkgbmFtZSBmb3IgdGhlIGdpdmVuIHBhdGguCiAgICAgKgogICAgICogRXhhbXBsZToKICAgICAqCiAgICAgKiBgYGAKICAgICAqIFBvbHltZXIuUGF0aC5yb290KCdmb28uYmFyLmJheicpIC8vICdmb28nCiAgICAgKiBQb2x5bWVyLlBhdGgucm9vdCgnZm9vJykgICAgICAgICAvLyAnZm9vJwogICAgICogYGBgCiAgICAgKgogICAgICogQG1lbWJlcm9mIFBvbHltZXIuUGF0aAogICAgICogQHBhcmFtIHtzdHJpbmd9IHBhdGggUGF0aCBzdHJpbmcKICAgICAqIEByZXR1cm4ge3N0cmluZ30gUm9vdCBwcm9wZXJ0eSBuYW1lCiAgICAgKi8KICAgIHJvb3Q6IGZ1bmN0aW9uKHBhdGgpIHsKICAgICAgbGV0IGRvdEluZGV4ID0gcGF0aC5pbmRleE9mKCcuJyk7CiAgICAgIGlmIChkb3RJbmRleCA9PT0gLTEpIHsKICAgICAgICByZXR1cm4gcGF0aDsKICAgICAgfQogICAgICByZXR1cm4gcGF0aC5zbGljZSgwLCBkb3RJbmRleCk7CiAgICB9LAoKICAgIC8qKgogICAgICogR2l2ZW4gYGJhc2VgIGlzIGBmb28uYmFyYCwgYGZvb2AgaXMgYW4gYW5jZXN0b3IsIGBmb28uYmFyYCBpcyBub3QKICAgICAqIFJldHVybnMgdHJ1ZSBpZiB0aGUgZ2l2ZW4gcGF0aCBpcyBhbiBhbmNlc3RvciBvZiB0aGUgYmFzZSBwYXRoLgogICAgICoKICAgICAqIEV4YW1wbGU6CiAgICAgKgogICAgICogYGBgCiAgICAgKiBQb2x5bWVyLlBhdGguaXNBbmNlc3RvcignZm9vLmJhcicsICdmb28nKSAgICAgICAgIC8vIHRydWUKICAgICAqIFBvbHltZXIuUGF0aC5pc0FuY2VzdG9yKCdmb28uYmFyJywgJ2Zvby5iYXInKSAgICAgLy8gZmFsc2UKICAgICAqIFBvbHltZXIuUGF0aC5pc0FuY2VzdG9yKCdmb28uYmFyJywgJ2Zvby5iYXIuYmF6JykgLy8gZmFsc2UKICAgICAqIGBgYAogICAgICoKICAgICAqIEBtZW1iZXJvZiBQb2x5bWVyLlBhdGgKICAgICAqIEBwYXJhbSB7c3RyaW5nfSBiYXNlIFBhdGggc3RyaW5nIHRvIHRlc3QgYWdhaW5zdC4KICAgICAqIEBwYXJhbSB7c3RyaW5nfSBwYXRoIFBhdGggc3RyaW5nIHRvIHRlc3QuCiAgICAgKiBAcmV0dXJuIHtib29sZWFufSBUcnVlIGlmIGBwYXRoYCBpcyBhbiBhbmNlc3RvciBvZiBgYmFzZWAuCiAgICAgKi8KICAgIGlzQW5jZXN0b3I6IGZ1bmN0aW9uKGJhc2UsIHBhdGgpIHsKICAgICAgLy8gICAgIGJhc2Uuc3RhcnRzV2l0aChwYXRoICsgJy4nKTsKICAgICAgcmV0dXJuIGJhc2UuaW5kZXhPZihwYXRoICsgJy4nKSA9PT0gMDsKICAgIH0sCgogICAgLyoqCiAgICAgKiBHaXZlbiBgYmFzZWAgaXMgYGZvby5iYXJgLCBgZm9vLmJhci5iYXpgIGlzIGFuIGRlc2NlbmRhbnQKICAgICAqCiAgICAgKiBFeGFtcGxlOgogICAgICoKICAgICAqIGBgYAogICAgICogUG9seW1lci5QYXRoLmlzRGVzY2VuZGFudCgnZm9vLmJhcicsICdmb28uYmFyLmJheicpIC8vIHRydWUKICAgICAqIFBvbHltZXIuUGF0aC5pc0Rlc2NlbmRhbnQoJ2Zvby5iYXInLCAnZm9vLmJhcicpICAgICAvLyBmYWxzZQogICAgICogUG9seW1lci5QYXRoLmlzRGVzY2VuZGFudCgnZm9vLmJhcicsICdmb28nKSAgICAgICAgIC8vIGZhbHNlCiAgICAgKiBgYGAKICAgICAqCiAgICAgKiBAbWVtYmVyb2YgUG9seW1lci5QYXRoCiAgICAgKiBAcGFyYW0ge3N0cmluZ30gYmFzZSBQYXRoIHN0cmluZyB0byB0ZXN0IGFnYWluc3QuCiAgICAgKiBAcGFyYW0ge3N0cmluZ30gcGF0aCBQYXRoIHN0cmluZyB0byB0ZXN0LgogICAgICogQHJldHVybiB7Ym9vbGVhbn0gVHJ1ZSBpZiBgcGF0aGAgaXMgYSBkZXNjZW5kYW50IG9mIGBiYXNlYC4KICAgICAqLwogICAgaXNEZXNjZW5kYW50OiBmdW5jdGlvbihiYXNlLCBwYXRoKSB7CiAgICAgIC8vICAgICBwYXRoLnN0YXJ0c1dpdGgoYmFzZSArICcuJyk7CiAgICAgIHJldHVybiBwYXRoLmluZGV4T2YoYmFzZSArICcuJykgPT09IDA7CiAgICB9LAoKICAgIC8qKgogICAgICogUmVwbGFjZXMgYSBwcmV2aW91cyBiYXNlIHBhdGggd2l0aCBhIG5ldyBiYXNlIHBhdGgsIHByZXNlcnZpbmcgdGhlCiAgICAgKiByZW1haW5kZXIgb2YgdGhlIHBhdGguCiAgICAgKgogICAgICogVXNlciBtdXN0IGVuc3VyZSBgcGF0aGAgaGFzIGEgcHJlZml4IG9mIGBiYXNlYC4KICAgICAqCiAgICAgKiBFeGFtcGxlOgogICAgICoKICAgICAqIGBgYAogICAgICogUG9seW1lci5QYXRoLnRyYW5zbGF0ZSgnZm9vLmJhcicsICd6b3QnLCAnZm9vLmJhci5iYXonKSAvLyAnem90LmJheicKICAgICAqIGBgYAogICAgICoKICAgICAqIEBtZW1iZXJvZiBQb2x5bWVyLlBhdGgKICAgICAqIEBwYXJhbSB7c3RyaW5nfSBiYXNlIEN1cnJlbnQgYmFzZSBzdHJpbmcgdG8gcmVtb3ZlCiAgICAgKiBAcGFyYW0ge3N0cmluZ30gbmV3QmFzZSBOZXcgYmFzZSBzdHJpbmcgdG8gcmVwbGFjZSB3aXRoCiAgICAgKiBAcGFyYW0ge3N0cmluZ30gcGF0aCBQYXRoIHRvIHRyYW5zbGF0ZQogICAgICogQHJldHVybiB7c3RyaW5nfSBUcmFuc2xhdGVkIHN0cmluZwogICAgICovCiAgICB0cmFuc2xhdGU6IGZ1bmN0aW9uKGJhc2UsIG5ld0Jhc2UsIHBhdGgpIHsKICAgICAgcmV0dXJuIG5ld0Jhc2UgKyBwYXRoLnNsaWNlKGJhc2UubGVuZ3RoKTsKICAgIH0sCgogICAgLyoqCiAgICAgKiBAcGFyYW0ge3N0cmluZ30gYmFzZSBQYXRoIHN0cmluZyB0byB0ZXN0IGFnYWluc3QKICAgICAqIEBwYXJhbSB7c3RyaW5nfSBwYXRoIFBhdGggc3RyaW5nIHRvIHRlc3QKICAgICAqIEByZXR1cm4ge2Jvb2xlYW59IFRydWUgaWYgYHBhdGhgIGlzIGVxdWFsIHRvIGBiYXNlYAogICAgICogQHRoaXMge1BhdGh9CiAgICAgKi8KICAgIG1hdGNoZXM6IGZ1bmN0aW9uKGJhc2UsIHBhdGgpIHsKICAgICAgcmV0dXJuIChiYXNlID09PSBwYXRoKSB8fAogICAgICAgICAgICAgdGhpcy5pc0FuY2VzdG9yKGJhc2UsIHBhdGgpIHx8CiAgICAgICAgICAgICB0aGlzLmlzRGVzY2VuZGFudChiYXNlLCBwYXRoKTsKICAgIH0sCgogICAgLyoqCiAgICAgKiBDb252ZXJ0cyBhcnJheS1iYXNlZCBwYXRocyB0byBmbGF0dGVuZWQgcGF0aC4gIFN0cmluZy1iYXNlZCBwYXRocwogICAgICogYXJlIHJldHVybmVkIGFzLWlzLgogICAgICoKICAgICAqIEV4YW1wbGU6CiAgICAgKgogICAgICogYGBgCiAgICAgKiBQb2x5bWVyLlBhdGgubm9ybWFsaXplKFsnZm9vLmJhcicsIDAsICdiYXonXSkgIC8vICdmb28uYmFyLjAuYmF6JwogICAgICogUG9seW1lci5QYXRoLm5vcm1hbGl6ZSgnZm9vLmJhci4wLmJheicpICAgICAgICAvLyAnZm9vLmJhci4wLmJheicKICAgICAqIGBgYAogICAgICoKICAgICAqIEBtZW1iZXJvZiBQb2x5bWVyLlBhdGgKICAgICAqIEBwYXJhbSB7c3RyaW5nIHwgIUFycmF5PHN0cmluZ3xudW1iZXI+fSBwYXRoIElucHV0IHBhdGgKICAgICAqIEByZXR1cm4ge3N0cmluZ30gRmxhdHRlbmVkIHBhdGgKICAgICAqLwogICAgbm9ybWFsaXplOiBmdW5jdGlvbihwYXRoKSB7CiAgICAgIGlmIChBcnJheS5pc0FycmF5KHBhdGgpKSB7CiAgICAgICAgbGV0IHBhcnRzID0gW107CiAgICAgICAgZm9yIChsZXQgaT0wOyBpPHBhdGgubGVuZ3RoOyBpKyspIHsKICAgICAgICAgIGxldCBhcmdzID0gcGF0aFtpXS50b1N0cmluZygpLnNwbGl0KCcuJyk7CiAgICAgICAgICBmb3IgKGxldCBqPTA7IGo8YXJncy5sZW5ndGg7IGorKykgewogICAgICAgICAgICBwYXJ0cy5wdXNoKGFyZ3Nbal0pOwogICAgICAgICAgfQogICAgICAgIH0KICAgICAgICByZXR1cm4gcGFydHMuam9pbignLicpOwogICAgICB9IGVsc2UgewogICAgICAgIHJldHVybiBwYXRoOwogICAgICB9CiAgICB9LAoKICAgIC8qKgogICAgICogU3BsaXRzIGEgcGF0aCBpbnRvIGFuIGFycmF5IG9mIHByb3BlcnR5IG5hbWVzLiBBY2NlcHRzIGVpdGhlciBhcnJheXMKICAgICAqIG9mIHBhdGggcGFydHMgb3Igc3RyaW5ncy4KICAgICAqCiAgICAgKiBFeGFtcGxlOgogICAgICoKICAgICAqIGBgYAogICAgICogUG9seW1lci5QYXRoLnNwbGl0KFsnZm9vLmJhcicsIDAsICdiYXonXSkgIC8vIFsnZm9vJywgJ2JhcicsICcwJywgJ2JheiddCiAgICAgKiBQb2x5bWVyLlBhdGguc3BsaXQoJ2Zvby5iYXIuMC5iYXonKSAgICAgICAgLy8gWydmb28nLCAnYmFyJywgJzAnLCAnYmF6J10KICAgICAqIGBgYAogICAgICoKICAgICAqIEBtZW1iZXJvZiBQb2x5bWVyLlBhdGgKICAgICAqIEBwYXJhbSB7c3RyaW5nIHwgIUFycmF5PHN0cmluZ3xudW1iZXI+fSBwYXRoIElucHV0IHBhdGgKICAgICAqIEByZXR1cm4geyFBcnJheTxzdHJpbmc+fSBBcnJheSBvZiBwYXRoIHBhcnRzCiAgICAgKiBAdGhpcyB7UGF0aH0KICAgICAqIEBzdXBwcmVzcyB7Y2hlY2tUeXBlc30KICAgICAqLwogICAgc3BsaXQ6IGZ1bmN0aW9uKHBhdGgpIHsKICAgICAgaWYgKEFycmF5LmlzQXJyYXkocGF0aCkpIHsKICAgICAgICByZXR1cm4gdGhpcy5ub3JtYWxpemUocGF0aCkuc3BsaXQoJy4nKTsKICAgICAgfQogICAgICByZXR1cm4gcGF0aC50b1N0cmluZygpLnNwbGl0KCcuJyk7CiAgICB9LAoKICAgIC8qKgogICAgICogUmVhZHMgYSB2YWx1ZSBmcm9tIGEgcGF0aC4gIElmIGFueSBzdWItcHJvcGVydHkgaW4gdGhlIHBhdGggaXMgYHVuZGVmaW5lZGAsCiAgICAgKiB0aGlzIG1ldGhvZCByZXR1cm5zIGB1bmRlZmluZWRgICh3aWxsIG5ldmVyIHRocm93LgogICAgICoKICAgICAqIEBtZW1iZXJvZiBQb2x5bWVyLlBhdGgKICAgICAqIEBwYXJhbSB7T2JqZWN0fSByb290IE9iamVjdCBmcm9tIHdoaWNoIHRvIGRlcmVmZXJlbmNlIHBhdGggZnJvbQogICAgICogQHBhcmFtIHtzdHJpbmcgfCAhQXJyYXk8c3RyaW5nfG51bWJlcj59IHBhdGggUGF0aCB0byByZWFkCiAgICAgKiBAcGFyYW0ge09iamVjdD19IGluZm8gSWYgYW4gb2JqZWN0IGlzIHByb3ZpZGVkIHRvIGBpbmZvYCwgdGhlIG5vcm1hbGl6ZWQKICAgICAqICAoZmxhdHRlbmVkKSBwYXRoIHdpbGwgYmUgc2V0IHRvIGBpbmZvLnBhdGhgLgogICAgICogQHJldHVybiB7Kn0gVmFsdWUgYXQgcGF0aCwgb3IgYHVuZGVmaW5lZGAgaWYgdGhlIHBhdGggY291bGQgbm90IGJlCiAgICAgKiAgZnVsbHkgZGVyZWZlcmVuY2VkLgogICAgICogQHRoaXMge1BhdGh9CiAgICAgKi8KICAgIGdldDogZnVuY3Rpb24ocm9vdCwgcGF0aCwgaW5mbykgewogICAgICBsZXQgcHJvcCA9IHJvb3Q7CiAgICAgIGxldCBwYXJ0cyA9IHRoaXMuc3BsaXQocGF0aCk7CiAgICAgIC8vIExvb3Agb3ZlciBwYXRoIHBhcnRzWzAuLm4tMV0gYW5kIGRlcmVmZXJlbmNlCiAgICAgIGZvciAobGV0IGk9MDsgaTxwYXJ0cy5sZW5ndGg7IGkrKykgewogICAgICAgIGlmICghcHJvcCkgewogICAgICAgICAgcmV0dXJuOwogICAgICAgIH0KICAgICAgICBsZXQgcGFydCA9IHBhcnRzW2ldOwogICAgICAgIHByb3AgPSBwcm9wW3BhcnRdOwogICAgICB9CiAgICAgIGlmIChpbmZvKSB7CiAgICAgICAgaW5mby5wYXRoID0gcGFydHMuam9pbignLicpOwogICAgICB9CiAgICAgIHJldHVybiBwcm9wOwogICAgfSwKCiAgICAvKioKICAgICAqIFNldHMgYSB2YWx1ZSB0byBhIHBhdGguICBJZiBhbnkgc3ViLXByb3BlcnR5IGluIHRoZSBwYXRoIGlzIGB1bmRlZmluZWRgLAogICAgICogdGhpcyBtZXRob2Qgd2lsbCBuby1vcC4KICAgICAqCiAgICAgKiBAbWVtYmVyb2YgUG9seW1lci5QYXRoCiAgICAgKiBAcGFyYW0ge09iamVjdH0gcm9vdCBPYmplY3QgZnJvbSB3aGljaCB0byBkZXJlZmVyZW5jZSBwYXRoIGZyb20KICAgICAqIEBwYXJhbSB7c3RyaW5nIHwgIUFycmF5PHN0cmluZ3xudW1iZXI+fSBwYXRoIFBhdGggdG8gc2V0CiAgICAgKiBAcGFyYW0geyp9IHZhbHVlIFZhbHVlIHRvIHNldCB0byBwYXRoCiAgICAgKiBAcmV0dXJuIHtzdHJpbmcgfCB1bmRlZmluZWR9IFRoZSBub3JtYWxpemVkIHZlcnNpb24gb2YgdGhlIGlucHV0IHBhdGgKICAgICAqIEB0aGlzIHtQYXRofQogICAgICovCiAgICBzZXQ6IGZ1bmN0aW9uKHJvb3QsIHBhdGgsIHZhbHVlKSB7CiAgICAgIGxldCBwcm9wID0gcm9vdDsKICAgICAgbGV0IHBhcnRzID0gdGhpcy5zcGxpdChwYXRoKTsKICAgICAgbGV0IGxhc3QgPSBwYXJ0c1twYXJ0cy5sZW5ndGgtMV07CiAgICAgIGlmIChwYXJ0cy5sZW5ndGggPiAxKSB7CiAgICAgICAgLy8gTG9vcCBvdmVyIHBhdGggcGFydHNbMC4ubi0yXSBhbmQgZGVyZWZlcmVuY2UKICAgICAgICBmb3IgKGxldCBpPTA7IGk8cGFydHMubGVuZ3RoLTE7IGkrKykgewogICAgICAgICAgbGV0IHBhcnQgPSBwYXJ0c1tpXTsKICAgICAgICAgIHByb3AgPSBwcm9wW3BhcnRdOwogICAgICAgICAgaWYgKCFwcm9wKSB7CiAgICAgICAgICAgIHJldHVybjsKICAgICAgICAgIH0KICAgICAgICB9CiAgICAgICAgLy8gU2V0IHZhbHVlIHRvIG9iamVjdCBhdCBlbmQgb2YgcGF0aAogICAgICAgIHByb3BbbGFzdF0gPSB2YWx1ZTsKICAgICAgfSBlbHNlIHsKICAgICAgICAvLyBTaW1wbGUgcHJvcGVydHkgc2V0CiAgICAgICAgcHJvcFtwYXRoXSA9IHZhbHVlOwogICAgICB9CiAgICAgIHJldHVybiBwYXJ0cy5qb2luKCcuJyk7CiAgICB9CgogIH07CgogIC8qKgogICAqIFJldHVybnMgdHJ1ZSBpZiB0aGUgZ2l2ZW4gc3RyaW5nIGlzIGEgc3RydWN0dXJlZCBkYXRhIHBhdGggKGhhcyBkb3RzKS4KICAgKgogICAqIFRoaXMgZnVuY3Rpb24gaXMgZGVwcmVjYXRlZC4gIFVzZSBgUG9seW1lci5QYXRoLmlzUGF0aGAgaW5zdGVhZC4KICAgKgogICAqIEV4YW1wbGU6CiAgICoKICAgKiBgYGAKICAgKiBQb2x5bWVyLlBhdGguaXNEZWVwKCdmb28uYmFyLmJheicpIC8vIHRydWUKICAgKiBQb2x5bWVyLlBhdGguaXNEZWVwKCdmb28nKSAgICAgICAgIC8vIGZhbHNlCiAgICogYGBgCiAgICoKICAgKiBAZGVwcmVjYXRlZAogICAqIEBtZW1iZXJvZiBQb2x5bWVyLlBhdGgKICAgKiBAcGFyYW0ge3N0cmluZ30gcGF0aCBQYXRoIHN0cmluZwogICAqIEByZXR1cm4ge2Jvb2xlYW59IFRydWUgaWYgdGhlIHN0cmluZyBjb250YWluZWQgb25lIG9yIG1vcmUgZG90cwogICAqLwogIFBhdGguaXNEZWVwID0gUGF0aC5pc1BhdGg7CgogIFBvbHltZXIuUGF0aCA9IFBhdGg7Cgp9KSgpOwo8L3NjcmlwdD48c2NyaXB0PgooZnVuY3Rpb24oKSB7CiAgJ3VzZSBzdHJpY3QnOwoKICBjb25zdCBjYXNlTWFwID0ge307CiAgY29uc3QgREFTSF9UT19DQU1FTCA9IC8tW2Etel0vZzsKICBjb25zdCBDQU1FTF9UT19EQVNIID0gLyhbQS1aXSkvZzsKCiAgLyoqCiAgICogTW9kdWxlIHdpdGggdXRpbGl0aWVzIGZvciBjb252ZXJ0aW5nIGJldHdlZW4gImRhc2gtY2FzZSIgYW5kICJjYW1lbENhc2UiCiAgICogaWRlbnRpZmllcnMuCiAgICoKICAgKiBAbmFtZXNwYWNlCiAgICogQG1lbWJlcm9mIFBvbHltZXIKICAgKiBAc3VtbWFyeSBNb2R1bGUgdGhhdCBwcm92aWRlcyB1dGlsaXRpZXMgZm9yIGNvbnZlcnRpbmcgYmV0d2VlbiAiZGFzaC1jYXNlIgogICAqICAgYW5kICJjYW1lbENhc2UiLgogICAqLwogIGNvbnN0IENhc2VNYXAgPSB7CgogICAgLyoqCiAgICAgKiBDb252ZXJ0cyAiZGFzaC1jYXNlIiBpZGVudGlmaWVyIChlLmcuIGBmb28tYmFyLWJhemApIHRvICJjYW1lbENhc2UiCiAgICAgKiAoZS5nLiBgZm9vQmFyQmF6YCkuCiAgICAgKgogICAgICogQG1lbWJlcm9mIFBvbHltZXIuQ2FzZU1hcAogICAgICogQHBhcmFtIHtzdHJpbmd9IGRhc2ggRGFzaC1jYXNlIGlkZW50aWZpZXIKICAgICAqIEByZXR1cm4ge3N0cmluZ30gQ2FtZWwtY2FzZSByZXByZXNlbnRhdGlvbiBvZiB0aGUgaWRlbnRpZmllcgogICAgICovCiAgICBkYXNoVG9DYW1lbENhc2UoZGFzaCkgewogICAgICByZXR1cm4gY2FzZU1hcFtkYXNoXSB8fCAoCiAgICAgICAgY2FzZU1hcFtkYXNoXSA9IGRhc2guaW5kZXhPZignLScpIDwgMCA/IGRhc2ggOiBkYXNoLnJlcGxhY2UoREFTSF9UT19DQU1FTCwKICAgICAgICAgIChtKSA9PiBtWzFdLnRvVXBwZXJDYXNlKCkKICAgICAgICApCiAgICAgICk7CiAgICB9LAoKICAgIC8qKgogICAgICogQ29udmVydHMgImNhbWVsQ2FzZSIgaWRlbnRpZmllciAoZS5nLiBgZm9vQmFyQmF6YCkgdG8gImRhc2gtY2FzZSIKICAgICAqIChlLmcuIGBmb28tYmFyLWJhemApLgogICAgICoKICAgICAqIEBtZW1iZXJvZiBQb2x5bWVyLkNhc2VNYXAKICAgICAqIEBwYXJhbSB7c3RyaW5nfSBjYW1lbCBDYW1lbC1jYXNlIGlkZW50aWZpZXIKICAgICAqIEByZXR1cm4ge3N0cmluZ30gRGFzaC1jYXNlIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBpZGVudGlmaWVyCiAgICAgKi8KICAgIGNhbWVsVG9EYXNoQ2FzZShjYW1lbCkgewogICAgICByZXR1cm4gY2FzZU1hcFtjYW1lbF0gfHwgKAogICAgICAgIGNhc2VNYXBbY2FtZWxdID0gY2FtZWwucmVwbGFjZShDQU1FTF9UT19EQVNILCAnLSQxJykudG9Mb3dlckNhc2UoKQogICAgICApOwogICAgfQoKICB9OwoKICBQb2x5bWVyLkNhc2VNYXAgPSBDYXNlTWFwOwp9KSgpOwo8L3NjcmlwdD48c2NyaXB0PgooZnVuY3Rpb24oKSB7CgogICd1c2Ugc3RyaWN0JzsKCiAgLy8gTWljcm90YXNrIGltcGxlbWVudGVkIHVzaW5nIE11dGF0aW9uIE9ic2VydmVyCiAgbGV0IG1pY3JvdGFza0N1cnJIYW5kbGUgPSAwOwogIGxldCBtaWNyb3Rhc2tMYXN0SGFuZGxlID0gMDsKICBsZXQgbWljcm90YXNrQ2FsbGJhY2tzID0gW107CiAgbGV0IG1pY3JvdGFza05vZGVDb250ZW50ID0gMDsKICBsZXQgbWljcm90YXNrTm9kZSA9IGRvY3VtZW50LmNyZWF0ZVRleHROb2RlKCcnKTsKICBuZXcgd2luZG93Lk11dGF0aW9uT2JzZXJ2ZXIobWljcm90YXNrRmx1c2gpLm9ic2VydmUobWljcm90YXNrTm9kZSwge2NoYXJhY3RlckRhdGE6IHRydWV9KTsKCiAgZnVuY3Rpb24gbWljcm90YXNrRmx1c2goKSB7CiAgICBjb25zdCBsZW4gPSBtaWNyb3Rhc2tDYWxsYmFja3MubGVuZ3RoOwogICAgZm9yIChsZXQgaSA9IDA7IGkgPCBsZW47IGkrKykgewogICAgICBsZXQgY2IgPSBtaWNyb3Rhc2tDYWxsYmFja3NbaV07CiAgICAgIGlmIChjYikgewogICAgICAgIHRyeSB7CiAgICAgICAgICBjYigpOwogICAgICAgIH0gY2F0Y2ggKGUpIHsKICAgICAgICAgIHNldFRpbWVvdXQoKCkgPT4geyB0aHJvdyBlOyB9KTsKICAgICAgICB9CiAgICAgIH0KICAgIH0KICAgIG1pY3JvdGFza0NhbGxiYWNrcy5zcGxpY2UoMCwgbGVuKTsKICAgIG1pY3JvdGFza0xhc3RIYW5kbGUgKz0gbGVuOwogIH0KCiAgLyoqCiAgICogTW9kdWxlIHRoYXQgcHJvdmlkZXMgYSBudW1iZXIgb2Ygc3RyYXRlZ2llcyBmb3IgZW5xdWV1aW5nIGFzeW5jaHJvbm91cwogICAqIHRhc2tzLiAgRWFjaCBzdWItbW9kdWxlIHByb3ZpZGVzIGEgc3RhbmRhcmQgYHJ1bihmbilgIGludGVyZmFjZSB0aGF0IHJldHVybnMgYQogICAqIGhhbmRsZSwgYW5kIGEgYGNhbmNlbChoYW5kbGUpYCBpbnRlcmZhY2UgZm9yIGNhbmNlbGluZyBhc3luYyB0YXNrcyBiZWZvcmUKICAgKiB0aGV5IHJ1bi4KICAgKgogICAqIEBuYW1lc3BhY2UKICAgKiBAbWVtYmVyb2YgUG9seW1lcgogICAqIEBzdW1tYXJ5IE1vZHVsZSB0aGF0IHByb3ZpZGVzIGEgbnVtYmVyIG9mIHN0cmF0ZWdpZXMgZm9yIGVucXVldWluZyBhc3luY2hyb25vdXMKICAgKiB0YXNrcy4KICAgKi8KICBQb2x5bWVyLkFzeW5jID0gewoKICAgIC8qKgogICAgICogQXN5bmMgaW50ZXJmYWNlIHdyYXBwZXIgYXJvdW5kIGBzZXRUaW1lb3V0YC4KICAgICAqCiAgICAgKiBAbmFtZXNwYWNlCiAgICAgKiBAbWVtYmVyb2YgUG9seW1lci5Bc3luYwogICAgICogQHN1bW1hcnkgQXN5bmMgaW50ZXJmYWNlIHdyYXBwZXIgYXJvdW5kIGBzZXRUaW1lb3V0YC4KICAgICAqLwogICAgdGltZU91dDogewogICAgICAvKioKICAgICAgICogUmV0dXJucyBhIHN1Yi1tb2R1bGUgd2l0aCB0aGUgYXN5bmMgaW50ZXJmYWNlIHByb3ZpZGluZyB0aGUgcHJvdmlkZWQKICAgICAgICogZGVsYXkuCiAgICAgICAqCiAgICAgICAqIEBtZW1iZXJvZiBQb2x5bWVyLkFzeW5jLnRpbWVPdXQKICAgICAgICogQHBhcmFtIHtudW1iZXI9fSBkZWxheSBUaW1lIHRvIHdhaXQgYmVmb3JlIGNhbGxpbmcgY2FsbGJhY2tzIGluIG1zCiAgICAgICAqIEByZXR1cm4geyFBc3luY0ludGVyZmFjZX0gQW4gYXN5bmMgdGltZW91dCBpbnRlcmZhY2UKICAgICAgICovCiAgICAgIGFmdGVyKGRlbGF5KSB7CiAgICAgICAgcmV0dXJuIHsKICAgICAgICAgIHJ1bihmbikgeyByZXR1cm4gd2luZG93LnNldFRpbWVvdXQoZm4sIGRlbGF5KTsgfSwKICAgICAgICAgIGNhbmNlbChoYW5kbGUpIHsKICAgICAgICAgICAgd2luZG93LmNsZWFyVGltZW91dChoYW5kbGUpOwogICAgICAgICAgfQogICAgICAgIH07CiAgICAgIH0sCiAgICAgIC8qKgogICAgICAgKiBFbnF1ZXVlcyBhIGZ1bmN0aW9uIGNhbGxlZCBpbiB0aGUgbmV4dCB0YXNrLgogICAgICAgKgogICAgICAgKiBAbWVtYmVyb2YgUG9seW1lci5Bc3luYy50aW1lT3V0CiAgICAgICAqIEBwYXJhbSB7IUZ1bmN0aW9ufSBmbiBDYWxsYmFjayB0byBydW4KICAgICAgICogQHBhcmFtIHtudW1iZXI9fSBkZWxheSBEZWxheSBpbiBtaWxsaXNlY29uZHMKICAgICAgICogQHJldHVybiB7bnVtYmVyfSBIYW5kbGUgdXNlZCBmb3IgY2FuY2VsaW5nIHRhc2sKICAgICAgICovCiAgICAgIHJ1bihmbiwgZGVsYXkpIHsKICAgICAgICByZXR1cm4gd2luZG93LnNldFRpbWVvdXQoZm4sIGRlbGF5KTsKICAgICAgfSwKICAgICAgLyoqCiAgICAgICAqIENhbmNlbHMgYSBwcmV2aW91c2x5IGVucXVldWVkIGB0aW1lT3V0YCBjYWxsYmFjay4KICAgICAgICoKICAgICAgICogQG1lbWJlcm9mIFBvbHltZXIuQXN5bmMudGltZU91dAogICAgICAgKiBAcGFyYW0ge251bWJlcn0gaGFuZGxlIEhhbmRsZSByZXR1cm5lZCBmcm9tIGBydW5gIG9mIGNhbGxiYWNrIHRvIGNhbmNlbAogICAgICAgKiBAcmV0dXJuIHt2b2lkfQogICAgICAgKi8KICAgICAgY2FuY2VsKGhhbmRsZSkgewogICAgICAgIHdpbmRvdy5jbGVhclRpbWVvdXQoaGFuZGxlKTsKICAgICAgfQogICAgfSwKCiAgICAvKioKICAgICAqIEFzeW5jIGludGVyZmFjZSB3cmFwcGVyIGFyb3VuZCBgcmVxdWVzdEFuaW1hdGlvbkZyYW1lYC4KICAgICAqCiAgICAgKiBAbmFtZXNwYWNlCiAgICAgKiBAbWVtYmVyb2YgUG9seW1lci5Bc3luYwogICAgICogQHN1bW1hcnkgQXN5bmMgaW50ZXJmYWNlIHdyYXBwZXIgYXJvdW5kIGByZXF1ZXN0QW5pbWF0aW9uRnJhbWVgLgogICAgICovCiAgICBhbmltYXRpb25GcmFtZTogewogICAgICAvKioKICAgICAgICogRW5xdWV1ZXMgYSBmdW5jdGlvbiBjYWxsZWQgYXQgYHJlcXVlc3RBbmltYXRpb25GcmFtZWAgdGltaW5nLgogICAgICAgKgogICAgICAgKiBAbWVtYmVyb2YgUG9seW1lci5Bc3luYy5hbmltYXRpb25GcmFtZQogICAgICAgKiBAcGFyYW0ge2Z1bmN0aW9uKG51bWJlcik6dm9pZH0gZm4gQ2FsbGJhY2sgdG8gcnVuCiAgICAgICAqIEByZXR1cm4ge251bWJlcn0gSGFuZGxlIHVzZWQgZm9yIGNhbmNlbGluZyB0YXNrCiAgICAgICAqLwogICAgICBydW4oZm4pIHsKICAgICAgICByZXR1cm4gd2luZG93LnJlcXVlc3RBbmltYXRpb25GcmFtZShmbik7CiAgICAgIH0sCiAgICAgIC8qKgogICAgICAgKiBDYW5jZWxzIGEgcHJldmlvdXNseSBlbnF1ZXVlZCBgYW5pbWF0aW9uRnJhbWVgIGNhbGxiYWNrLgogICAgICAgKgogICAgICAgKiBAbWVtYmVyb2YgUG9seW1lci5Bc3luYy5hbmltYXRpb25GcmFtZQogICAgICAgKiBAcGFyYW0ge251bWJlcn0gaGFuZGxlIEhhbmRsZSByZXR1cm5lZCBmcm9tIGBydW5gIG9mIGNhbGxiYWNrIHRvIGNhbmNlbAogICAgICAgKiBAcmV0dXJuIHt2b2lkfQogICAgICAgKi8KICAgICAgY2FuY2VsKGhhbmRsZSkgewogICAgICAgIHdpbmRvdy5jYW5jZWxBbmltYXRpb25GcmFtZShoYW5kbGUpOwogICAgICB9CiAgICB9LAoKICAgIC8qKgogICAgICogQXN5bmMgaW50ZXJmYWNlIHdyYXBwZXIgYXJvdW5kIGByZXF1ZXN0SWRsZUNhbGxiYWNrYC4gIEZhbGxzIGJhY2sgdG8KICAgICAqIGBzZXRUaW1lb3V0YCBvbiBicm93c2VycyB0aGF0IGRvIG5vdCBzdXBwb3J0IGByZXF1ZXN0SWRsZUNhbGxiYWNrYC4KICAgICAqCiAgICAgKiBAbmFtZXNwYWNlCiAgICAgKiBAbWVtYmVyb2YgUG9seW1lci5Bc3luYwogICAgICogQHN1bW1hcnkgQXN5bmMgaW50ZXJmYWNlIHdyYXBwZXIgYXJvdW5kIGByZXF1ZXN0SWRsZUNhbGxiYWNrYC4KICAgICAqLwogICAgaWRsZVBlcmlvZDogewogICAgICAvKioKICAgICAgICogRW5xdWV1ZXMgYSBmdW5jdGlvbiBjYWxsZWQgYXQgYHJlcXVlc3RJZGxlQ2FsbGJhY2tgIHRpbWluZy4KICAgICAgICoKICAgICAgICogQG1lbWJlcm9mIFBvbHltZXIuQXN5bmMuaWRsZVBlcmlvZAogICAgICAgKiBAcGFyYW0ge2Z1bmN0aW9uKCFJZGxlRGVhZGxpbmUpOnZvaWR9IGZuIENhbGxiYWNrIHRvIHJ1bgogICAgICAgKiBAcmV0dXJuIHtudW1iZXJ9IEhhbmRsZSB1c2VkIGZvciBjYW5jZWxpbmcgdGFzawogICAgICAgKi8KICAgICAgcnVuKGZuKSB7CiAgICAgICAgcmV0dXJuIHdpbmRvdy5yZXF1ZXN0SWRsZUNhbGxiYWNrID8KICAgICAgICAgIHdpbmRvdy5yZXF1ZXN0SWRsZUNhbGxiYWNrKGZuKSA6CiAgICAgICAgICB3aW5kb3cuc2V0VGltZW91dChmbiwgMTYpOwogICAgICB9LAogICAgICAvKioKICAgICAgICogQ2FuY2VscyBhIHByZXZpb3VzbHkgZW5xdWV1ZWQgYGlkbGVQZXJpb2RgIGNhbGxiYWNrLgogICAgICAgKgogICAgICAgKiBAbWVtYmVyb2YgUG9seW1lci5Bc3luYy5pZGxlUGVyaW9kCiAgICAgICAqIEBwYXJhbSB7bnVtYmVyfSBoYW5kbGUgSGFuZGxlIHJldHVybmVkIGZyb20gYHJ1bmAgb2YgY2FsbGJhY2sgdG8gY2FuY2VsCiAgICAgICAqIEByZXR1cm4ge3ZvaWR9CiAgICAgICAqLwogICAgICBjYW5jZWwoaGFuZGxlKSB7CiAgICAgICAgd2luZG93LmNhbmNlbElkbGVDYWxsYmFjayA/CiAgICAgICAgICB3aW5kb3cuY2FuY2VsSWRsZUNhbGxiYWNrKGhhbmRsZSkgOgogICAgICAgICAgd2luZG93LmNsZWFyVGltZW91dChoYW5kbGUpOwogICAgICB9CiAgICB9LAoKICAgIC8qKgogICAgICogQXN5bmMgaW50ZXJmYWNlIGZvciBlbnF1ZXVpbmcgY2FsbGJhY2tzIHRoYXQgcnVuIGF0IG1pY3JvdGFzayB0aW1pbmcuCiAgICAgKgogICAgICogTm90ZSB0aGF0IG1pY3JvdGFzayB0aW1pbmcgaXMgYWNoaWV2ZWQgdmlhIGEgc2luZ2xlIGBNdXRhdGlvbk9ic2VydmVyYCwKICAgICAqIGFuZCB0aHVzIGNhbGxiYWNrcyBlbnF1ZXVlZCB3aXRoIHRoaXMgQVBJIHdpbGwgYWxsIHJ1biBpbiBhIHNpbmdsZQogICAgICogYmF0Y2gsIGFuZCBub3QgaW50ZXJsZWF2ZWQgd2l0aCBvdGhlciBtaWNyb3Rhc2tzIHN1Y2ggYXMgcHJvbWlzZXMuCiAgICAgKiBQcm9taXNlcyBhcmUgYXZvaWRlZCBhcyBhbiBpbXBsZW1lbnRhdGlvbiBjaG9pY2UgZm9yIHRoZSB0aW1lIGJlaW5nCiAgICAgKiBkdWUgdG8gU2FmYXJpIGJ1Z3MgdGhhdCBjYXVzZSBQcm9taXNlcyB0byBsYWNrIG1pY3JvdGFzayBndWFyYW50ZWVzLgogICAgICoKICAgICAqIEBuYW1lc3BhY2UKICAgICAqIEBtZW1iZXJvZiBQb2x5bWVyLkFzeW5jCiAgICAgKiBAc3VtbWFyeSBBc3luYyBpbnRlcmZhY2UgZm9yIGVucXVldWluZyBjYWxsYmFja3MgdGhhdCBydW4gYXQgbWljcm90YXNrCiAgICAgKiAgIHRpbWluZy4KICAgICAqLwogICAgbWljcm9UYXNrOiB7CgogICAgICAvKioKICAgICAgICogRW5xdWV1ZXMgYSBmdW5jdGlvbiBjYWxsZWQgYXQgbWljcm90YXNrIHRpbWluZy4KICAgICAgICoKICAgICAgICogQG1lbWJlcm9mIFBvbHltZXIuQXN5bmMubWljcm9UYXNrCiAgICAgICAqIEBwYXJhbSB7IUZ1bmN0aW9uPX0gY2FsbGJhY2sgQ2FsbGJhY2sgdG8gcnVuCiAgICAgICAqIEByZXR1cm4ge251bWJlcn0gSGFuZGxlIHVzZWQgZm9yIGNhbmNlbGluZyB0YXNrCiAgICAgICAqLwogICAgICBydW4oY2FsbGJhY2spIHsKICAgICAgICBtaWNyb3Rhc2tOb2RlLnRleHRDb250ZW50ID0gbWljcm90YXNrTm9kZUNvbnRlbnQrKzsKICAgICAgICBtaWNyb3Rhc2tDYWxsYmFja3MucHVzaChjYWxsYmFjayk7CiAgICAgICAgcmV0dXJuIG1pY3JvdGFza0N1cnJIYW5kbGUrKzsKICAgICAgfSwKCiAgICAgIC8qKgogICAgICAgKiBDYW5jZWxzIGEgcHJldmlvdXNseSBlbnF1ZXVlZCBgbWljcm9UYXNrYCBjYWxsYmFjay4KICAgICAgICoKICAgICAgICogQG1lbWJlcm9mIFBvbHltZXIuQXN5bmMubWljcm9UYXNrCiAgICAgICAqIEBwYXJhbSB7bnVtYmVyfSBoYW5kbGUgSGFuZGxlIHJldHVybmVkIGZyb20gYHJ1bmAgb2YgY2FsbGJhY2sgdG8gY2FuY2VsCiAgICAgICAqIEByZXR1cm4ge3ZvaWR9CiAgICAgICAqLwogICAgICBjYW5jZWwoaGFuZGxlKSB7CiAgICAgICAgY29uc3QgaWR4ID0gaGFuZGxlIC0gbWljcm90YXNrTGFzdEhhbmRsZTsKICAgICAgICBpZiAoaWR4ID49IDApIHsKICAgICAgICAgIGlmICghbWljcm90YXNrQ2FsbGJhY2tzW2lkeF0pIHsKICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdpbnZhbGlkIGFzeW5jIGhhbmRsZTogJyArIGhhbmRsZSk7CiAgICAgICAgICB9CiAgICAgICAgICBtaWNyb3Rhc2tDYWxsYmFja3NbaWR4XSA9IG51bGw7CiAgICAgICAgfQogICAgICB9CgogICAgfQogIH07Cgp9KSgpOwo8L3NjcmlwdD48c2NyaXB0PgogIChmdW5jdGlvbiAoKSB7CgogICAgJ3VzZSBzdHJpY3QnOwoKICAgIC8qKiBAY29uc3QgeyFBc3luY0ludGVyZmFjZX0gKi8KICAgIGNvbnN0IG1pY3JvdGFzayA9IFBvbHltZXIuQXN5bmMubWljcm9UYXNrOwoKICAgIC8qKgogICAgICogRWxlbWVudCBjbGFzcyBtaXhpbiB0aGF0IHByb3ZpZGVzIGJhc2ljIG1ldGEtcHJvZ3JhbW1pbmcgZm9yIGNyZWF0aW5nIG9uZQogICAgICogb3IgbW9yZSBwcm9wZXJ0eSBhY2Nlc3NvcnMgKGdldHRlci9zZXR0ZXIgcGFpcikgdGhhdCBlbnF1ZXVlIGFuIGFzeW5jCiAgICAgKiAoYmF0Y2hlZCkgYF9wcm9wZXJ0aWVzQ2hhbmdlZGAgY2FsbGJhY2suCiAgICAgKgogICAgICogRm9yIGJhc2ljIHVzYWdlIG9mIHRoaXMgbWl4aW4sIGNhbGwgYE15Q2xhc3MuY3JlYXRlUHJvcGVydGllcyhwcm9wcylgCiAgICAgKiBvbmNlIGF0IGNsYXNzIGRlZmluaXRpb24gdGltZSB0byBjcmVhdGUgcHJvcGVydHkgYWNjZXNzb3JzIGZvciBwcm9wZXJ0aWVzCiAgICAgKiBuYW1lZCBpbiBwcm9wcywgaW1wbGVtZW50IGBfcHJvcGVydGllc0NoYW5nZWRgIHRvIHJlYWN0IGFzIGRlc2lyZWQgdG8KICAgICAqIHByb3BlcnR5IGNoYW5nZXMsIGFuZCBpbXBsZW1lbnQgYHN0YXRpYyBnZXQgb2JzZXJ2ZWRBdHRyaWJ1dGVzKClgIGFuZAogICAgICogaW5jbHVkZSBsb3dlcmNhc2UgdmVyc2lvbnMgb2YgYW55IHByb3BlcnR5IG5hbWVzIHRoYXQgc2hvdWxkIGJlIHNldCBmcm9tCiAgICAgKiBhdHRyaWJ1dGVzLiBMYXN0LCBjYWxsIGB0aGlzLl9lbmFibGVQcm9wZXJ0aWVzKClgIGluIHRoZSBlbGVtZW50J3MKICAgICAqIGBjb25uZWN0ZWRDYWxsYmFja2AgdG8gZW5hYmxlIHRoZSBhY2Nlc3NvcnMuCiAgICAgKgogICAgICogQG1peGluRnVuY3Rpb24KICAgICAqIEBwb2x5bWVyCiAgICAgKiBAbWVtYmVyb2YgUG9seW1lcgogICAgICogQHN1bW1hcnkgRWxlbWVudCBjbGFzcyBtaXhpbiBmb3IgcmVhY3RpbmcgdG8gcHJvcGVydHkgY2hhbmdlcyBmcm9tCiAgICAgKiAgIGdlbmVyYXRlZCBwcm9wZXJ0eSBhY2Nlc3NvcnMuCiAgICAgKi8KICAgIFBvbHltZXIuUHJvcGVydGllc0NoYW5nZWQgPSBQb2x5bWVyLmRlZHVwaW5nTWl4aW4oc3VwZXJDbGFzcyA9PiB7CgogICAgICAvKioKICAgICAgICogQHBvbHltZXIKICAgICAgICogQG1peGluQ2xhc3MKICAgICAgICogQGV4dGVuZHMge3N1cGVyQ2xhc3N9CiAgICAgICAqIEBpbXBsZW1lbnRzIHtQb2x5bWVyX1Byb3BlcnRpZXNDaGFuZ2VkfQogICAgICAgKiBAdW5yZXN0cmljdGVkCiAgICAgICAqLwogICAgICBjbGFzcyBQcm9wZXJ0aWVzQ2hhbmdlZCBleHRlbmRzIHN1cGVyQ2xhc3MgewoKICAgICAgICAvKioKICAgICAgICAgKiBDcmVhdGVzIHByb3BlcnR5IGFjY2Vzc29ycyBmb3IgdGhlIGdpdmVuIHByb3BlcnR5IG5hbWVzLgogICAgICAgICAqIEBwYXJhbSB7IU9iamVjdH0gcHJvcHMgT2JqZWN0IHdob3NlIGtleXMgYXJlIG5hbWVzIG9mIGFjY2Vzc29ycy4KICAgICAgICAgKiBAcmV0dXJuIHt2b2lkfQogICAgICAgICAqIEBwcm90ZWN0ZWQKICAgICAgICAgKi8KICAgICAgICBzdGF0aWMgY3JlYXRlUHJvcGVydGllcyhwcm9wcykgewogICAgICAgICAgY29uc3QgcHJvdG8gPSB0aGlzLnByb3RvdHlwZTsKICAgICAgICAgIGZvciAobGV0IHByb3AgaW4gcHJvcHMpIHsKICAgICAgICAgICAgLy8gZG9uJ3Qgc3RvbXAgYW4gZXhpc3RpbmcgYWNjZXNzb3IKICAgICAgICAgICAgaWYgKCEocHJvcCBpbiBwcm90bykpIHsKICAgICAgICAgICAgICBwcm90by5fY3JlYXRlUHJvcGVydHlBY2Nlc3Nvcihwcm9wKTsKICAgICAgICAgICAgfQogICAgICAgICAgfQogICAgICAgIH0KCiAgICAgICAgLyoqCiAgICAgICAgICogUmV0dXJucyBhbiBhdHRyaWJ1dGUgbmFtZSB0aGF0IGNvcnJlc3BvbmRzIHRvIHRoZSBnaXZlbiBwcm9wZXJ0eS4KICAgICAgICAgKiBUaGUgYXR0cmlidXRlIG5hbWUgaXMgdGhlIGxvd2VyY2FzZWQgcHJvcGVydHkgbmFtZS4gT3ZlcnJpZGUgdG8KICAgICAgICAgKiBjdXN0b21pemUgdGhpcyBtYXBwaW5nLgogICAgICAgICAqIEBwYXJhbSB7c3RyaW5nfSBwcm9wZXJ0eSBQcm9wZXJ0eSB0byBjb252ZXJ0CiAgICAgICAgICogQHJldHVybiB7c3RyaW5nfSBBdHRyaWJ1dGUgbmFtZSBjb3JyZXNwb25kaW5nIHRvIHRoZSBnaXZlbiBwcm9wZXJ0eS4KICAgICAgICAgKgogICAgICAgICAqIEBwcm90ZWN0ZWQKICAgICAgICAgKi8KICAgICAgICBzdGF0aWMgYXR0cmlidXRlTmFtZUZvclByb3BlcnR5KHByb3BlcnR5KSB7CiAgICAgICAgICByZXR1cm4gcHJvcGVydHkudG9Mb3dlckNhc2UoKTsKICAgICAgICB9CgogICAgICAgIC8qKgogICAgICAgICAqIE92ZXJyaWRlIHBvaW50IHRvIHByb3ZpZGUgYSB0eXBlIHRvIHdoaWNoIHRvIGRlc2VyaWFsaXplIGEgdmFsdWUgdG8KICAgICAgICAgKiBhIGdpdmVuIHByb3BlcnR5LgogICAgICAgICAqIEBwYXJhbSB7c3RyaW5nfSBuYW1lIE5hbWUgb2YgcHJvcGVydHkKICAgICAgICAgKgogICAgICAgICAqIEBwcm90ZWN0ZWQKICAgICAgICAgKi8KICAgICAgICBzdGF0aWMgdHlwZUZvclByb3BlcnR5KG5hbWUpIHsgfSAvL2VzbGludC1kaXNhYmxlLWxpbmUgbm8tdW51c2VkLXZhcnMKCiAgICAgICAgLyoqCiAgICAgICAgICogQ3JlYXRlcyBhIHNldHRlci9nZXR0ZXIgcGFpciBmb3IgdGhlIG5hbWVkIHByb3BlcnR5IHdpdGggaXRzIG93bgogICAgICAgICAqIGxvY2FsIHN0b3JhZ2UuICBUaGUgZ2V0dGVyIHJldHVybnMgdGhlIHZhbHVlIGluIHRoZSBsb2NhbCBzdG9yYWdlLAogICAgICAgICAqIGFuZCB0aGUgc2V0dGVyIGNhbGxzIGBfc2V0UHJvcGVydHlgLCB3aGljaCB1cGRhdGVzIHRoZSBsb2NhbCBzdG9yYWdlCiAgICAgICAgICogZm9yIHRoZSBwcm9wZXJ0eSBhbmQgZW5xdWV1ZXMgYSBgX3Byb3BlcnRpZXNDaGFuZ2VkYCBjYWxsYmFjay4KICAgICAgICAgKgogICAgICAgICAqIFRoaXMgbWV0aG9kIG1heSBiZSBjYWxsZWQgb24gYSBwcm90b3R5cGUgb3IgYW4gaW5zdGFuY2UuICBDYWxsaW5nCiAgICAgICAgICogdGhpcyBtZXRob2QgbWF5IG92ZXJ3cml0ZSBhIHByb3BlcnR5IHZhbHVlIHRoYXQgYWxyZWFkeSBleGlzdHMgb24KICAgICAgICAgKiB0aGUgcHJvdG90eXBlL2luc3RhbmNlIGJ5IGNyZWF0aW5nIHRoZSBhY2Nlc3Nvci4KICAgICAgICAgKgogICAgICAgICAqIEBwYXJhbSB7c3RyaW5nfSBwcm9wZXJ0eSBOYW1lIG9mIHRoZSBwcm9wZXJ0eQogICAgICAgICAqIEBwYXJhbSB7Ym9vbGVhbj19IHJlYWRPbmx5IFdoZW4gdHJ1ZSwgbm8gc2V0dGVyIGlzIGNyZWF0ZWQ7IHRoZQogICAgICAgICAqICAgcHJvdGVjdGVkIGBfc2V0UHJvcGVydHlgIGZ1bmN0aW9uIG11c3QgYmUgdXNlZCB0byBzZXQgdGhlIHByb3BlcnR5CiAgICAgICAgICogQHJldHVybiB7dm9pZH0KICAgICAgICAgKiBAcHJvdGVjdGVkCiAgICAgICAgICovCiAgICAgICAgX2NyZWF0ZVByb3BlcnR5QWNjZXNzb3IocHJvcGVydHksIHJlYWRPbmx5KSB7CiAgICAgICAgICB0aGlzLl9hZGRQcm9wZXJ0eVRvQXR0cmlidXRlTWFwKHByb3BlcnR5KTsKICAgICAgICAgIGlmICghdGhpcy5oYXNPd25Qcm9wZXJ0eSgnX19kYXRhSGFzQWNjZXNzb3InKSkgewogICAgICAgICAgICB0aGlzLl9fZGF0YUhhc0FjY2Vzc29yID0gT2JqZWN0LmFzc2lnbih7fSwgdGhpcy5fX2RhdGFIYXNBY2Nlc3Nvcik7CiAgICAgICAgICB9CiAgICAgICAgICBpZiAoIXRoaXMuX19kYXRhSGFzQWNjZXNzb3JbcHJvcGVydHldKSB7CiAgICAgICAgICAgIHRoaXMuX19kYXRhSGFzQWNjZXNzb3JbcHJvcGVydHldID0gdHJ1ZTsKICAgICAgICAgICAgdGhpcy5fZGVmaW5lUHJvcGVydHlBY2Nlc3Nvcihwcm9wZXJ0eSwgcmVhZE9ubHkpOwogICAgICAgICAgfQogICAgICAgIH0KCiAgICAgICAgLyoqCiAgICAgICAgICogQWRkcyB0aGUgZ2l2ZW4gYHByb3BlcnR5YCB0byBhIG1hcCBtYXRjaGluZyBhdHRyaWJ1dGUgbmFtZXMKICAgICAgICAgKiB0byBwcm9wZXJ0eSBuYW1lcywgdXNpbmcgYGF0dHJpYnV0ZU5hbWVGb3JQcm9wZXJ0eWAuIFRoaXMgbWFwIGlzCiAgICAgICAgICogdXNlZCB3aGVuIGRlc2VyaWFsaXppbmcgYXR0cmlidXRlIHZhbHVlcyB0byBwcm9wZXJ0aWVzLgogICAgICAgICAqCiAgICAgICAgICogQHBhcmFtIHtzdHJpbmd9IHByb3BlcnR5IE5hbWUgb2YgdGhlIHByb3BlcnR5CiAgICAgICAgICovCiAgICAgICAgX2FkZFByb3BlcnR5VG9BdHRyaWJ1dGVNYXAocHJvcGVydHkpIHsKICAgICAgICAgIGlmICghdGhpcy5oYXNPd25Qcm9wZXJ0eSgnX19kYXRhQXR0cmlidXRlcycpKSB7CiAgICAgICAgICAgIHRoaXMuX19kYXRhQXR0cmlidXRlcyA9IE9iamVjdC5hc3NpZ24oe30sIHRoaXMuX19kYXRhQXR0cmlidXRlcyk7CiAgICAgICAgICB9CiAgICAgICAgICBpZiAoIXRoaXMuX19kYXRhQXR0cmlidXRlc1twcm9wZXJ0eV0pIHsKICAgICAgICAgICAgY29uc3QgYXR0ciA9IHRoaXMuY29uc3RydWN0b3IuYXR0cmlidXRlTmFtZUZvclByb3BlcnR5KHByb3BlcnR5KTsKICAgICAgICAgICAgdGhpcy5fX2RhdGFBdHRyaWJ1dGVzW2F0dHJdID0gcHJvcGVydHk7CiAgICAgICAgICB9CiAgICAgICAgfQoKICAgICAgICAvKioKICAgICAgICAgKiBEZWZpbmVzIGEgcHJvcGVydHkgYWNjZXNzb3IgZm9yIHRoZSBnaXZlbiBwcm9wZXJ0eS4KICAgICAgICAgKiBAcGFyYW0ge3N0cmluZ30gcHJvcGVydHkgTmFtZSBvZiB0aGUgcHJvcGVydHkKICAgICAgICAgKiBAcGFyYW0ge2Jvb2xlYW49fSByZWFkT25seSBXaGVuIHRydWUsIG5vIHNldHRlciBpcyBjcmVhdGVkCiAgICAgICAgICogQHJldHVybiB7dm9pZH0KICAgICAgICAgKi8KICAgICAgICAgX2RlZmluZVByb3BlcnR5QWNjZXNzb3IocHJvcGVydHksIHJlYWRPbmx5KSB7CiAgICAgICAgICBPYmplY3QuZGVmaW5lUHJvcGVydHkodGhpcywgcHJvcGVydHksIHsKICAgICAgICAgICAgLyogZXNsaW50LWRpc2FibGUgdmFsaWQtanNkb2MgKi8KICAgICAgICAgICAgLyoqIEB0aGlzIHtQcm9wZXJ0aWVzQ2hhbmdlZH0gKi8KICAgICAgICAgICAgZ2V0KCkgewogICAgICAgICAgICAgIHJldHVybiB0aGlzLl9nZXRQcm9wZXJ0eShwcm9wZXJ0eSk7CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgIC8qKiBAdGhpcyB7UHJvcGVydGllc0NoYW5nZWR9ICovCiAgICAgICAgICAgIHNldDogcmVhZE9ubHkgPyBmdW5jdGlvbiAoKSB7fSA6IGZ1bmN0aW9uICh2YWx1ZSkgewogICAgICAgICAgICAgIHRoaXMuX3NldFByb3BlcnR5KHByb3BlcnR5LCB2YWx1ZSk7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgLyogZXNsaW50LWVuYWJsZSAqLwogICAgICAgICAgfSk7CiAgICAgICAgfQoKICAgICAgICBjb25zdHJ1Y3RvcigpIHsKICAgICAgICAgIHN1cGVyKCk7CiAgICAgICAgICB0aGlzLl9fZGF0YUVuYWJsZWQgPSBmYWxzZTsKICAgICAgICAgIHRoaXMuX19kYXRhUmVhZHkgPSBmYWxzZTsKICAgICAgICAgIHRoaXMuX19kYXRhSW52YWxpZCA9IGZhbHNlOwogICAgICAgICAgdGhpcy5fX2RhdGEgPSB7fTsKICAgICAgICAgIHRoaXMuX19kYXRhUGVuZGluZyA9IG51bGw7CiAgICAgICAgICB0aGlzLl9fZGF0YU9sZCA9IG51bGw7CiAgICAgICAgICB0aGlzLl9fZGF0YUluc3RhbmNlUHJvcHMgPSBudWxsOwogICAgICAgICAgdGhpcy5fX3NlcmlhbGl6aW5nID0gZmFsc2U7CiAgICAgICAgICB0aGlzLl9pbml0aWFsaXplUHJvcGVydGllcygpOwogICAgICAgIH0KCiAgICAgICAgLyoqCiAgICAgICAgICogTGlmZWN5Y2xlIGNhbGxiYWNrIGNhbGxlZCB3aGVuIHByb3BlcnRpZXMgYXJlIGVuYWJsZWQgdmlhCiAgICAgICAgICogYF9lbmFibGVQcm9wZXJ0aWVzYC4KICAgICAgICAgKgogICAgICAgICAqIFVzZXJzIG1heSBvdmVycmlkZSB0aGlzIGZ1bmN0aW9uIHRvIGltcGxlbWVudCBiZWhhdmlvciB0aGF0IGlzCiAgICAgICAgICogZGVwZW5kZW50IG9uIHRoZSBlbGVtZW50IGhhdmluZyBpdHMgcHJvcGVydHkgZGF0YSBpbml0aWFsaXplZCwgZS5nLgogICAgICAgICAqIGZyb20gZGVmYXVsdHMgKGluaXRpYWxpemVkIGZyb20gYGNvbnN0cnVjdG9yYCwgYF9pbml0aWFsaXplUHJvcGVydGllc2ApLAogICAgICAgICAqIGBhdHRyaWJ1dGVDaGFuZ2VkQ2FsbGJhY2tgLCBvciB2YWx1ZXMgcHJvcGFnYXRlZCBmcm9tIGhvc3QgZS5nLiB2aWEKICAgICAgICAgKiBiaW5kaW5ncy4gIGBzdXBlci5yZWFkeSgpYCBtdXN0IGJlIGNhbGxlZCB0byBlbnN1cmUgdGhlIGRhdGEgc3lzdGVtCiAgICAgICAgICogYmVjb21lcyBlbmFibGVkLgogICAgICAgICAqCiAgICAgICAgICogQHJldHVybiB7dm9pZH0KICAgICAgICAgKiBAcHVibGljCiAgICAgICAgICovCiAgICAgICAgcmVhZHkoKSB7CiAgICAgICAgICB0aGlzLl9fZGF0YVJlYWR5ID0gdHJ1ZTsKICAgICAgICAgIHRoaXMuX2ZsdXNoUHJvcGVydGllcygpOwogICAgICAgIH0KCiAgICAgICAgLyoqCiAgICAgICAgICogSW5pdGlhbGl6ZXMgdGhlIGxvY2FsIHN0b3JhZ2UgZm9yIHByb3BlcnR5IGFjY2Vzc29ycy4KICAgICAgICAgKgogICAgICAgICAqIFByb3ZpZGVkIGFzIGFuIG92ZXJyaWRlIHBvaW50IGZvciBwZXJmb3JtaW5nIGFueSBzZXR1cCB3b3JrIHByaW9yCiAgICAgICAgICogdG8gaW5pdGlhbGl6aW5nIHRoZSBwcm9wZXJ0eSBhY2Nlc3NvciBzeXN0ZW0uCiAgICAgICAgICoKICAgICAgICAgKiBAcmV0dXJuIHt2b2lkfQogICAgICAgICAqIEBwcm90ZWN0ZWQKICAgICAgICAgKi8KICAgICAgICBfaW5pdGlhbGl6ZVByb3BlcnRpZXMoKSB7CiAgICAgICAgICAvLyBDYXB0dXJlIGluc3RhbmNlIHByb3BlcnRpZXM7IHRoZXNlIHdpbGwgYmUgc2V0IGludG8gYWNjZXNzb3JzCiAgICAgICAgICAvLyBkdXJpbmcgZmlyc3QgZmx1c2guIERvbid0IHNldCB0aGVtIGhlcmUsIHNpbmNlIHdlIHdhbnQKICAgICAgICAgIC8vIHRoZXNlIHRvIG92ZXJ3cml0ZSBkZWZhdWx0cy9jb25zdHJ1Y3RvciBhc3NpZ25tZW50cwogICAgICAgICAgZm9yIChsZXQgcCBpbiB0aGlzLl9fZGF0YUhhc0FjY2Vzc29yKSB7CiAgICAgICAgICAgIGlmICh0aGlzLmhhc093blByb3BlcnR5KHApKSB7CiAgICAgICAgICAgICAgdGhpcy5fX2RhdGFJbnN0YW5jZVByb3BzID0gdGhpcy5fX2RhdGFJbnN0YW5jZVByb3BzIHx8IHt9OwogICAgICAgICAgICAgIHRoaXMuX19kYXRhSW5zdGFuY2VQcm9wc1twXSA9IHRoaXNbcF07CiAgICAgICAgICAgICAgZGVsZXRlIHRoaXNbcF07CiAgICAgICAgICAgIH0KICAgICAgICAgIH0KICAgICAgICB9CgogICAgICAgIC8qKgogICAgICAgICAqIENhbGxlZCBhdCByZWFkeSB0aW1lIHdpdGggYmFnIG9mIGluc3RhbmNlIHByb3BlcnRpZXMgdGhhdCBvdmVyd3JvdGUKICAgICAgICAgKiBhY2Nlc3NvcnMgd2hlbiB0aGUgZWxlbWVudCB1cGdyYWRlZC4KICAgICAgICAgKgogICAgICAgICAqIFRoZSBkZWZhdWx0IGltcGxlbWVudGF0aW9uIHNldHMgdGhlc2UgcHJvcGVydGllcyBiYWNrIGludG8gdGhlCiAgICAgICAgICogc2V0dGVyIGF0IHJlYWR5IHRpbWUuICBUaGlzIG1ldGhvZCBpcyBwcm92aWRlZCBhcyBhbiBvdmVycmlkZQogICAgICAgICAqIHBvaW50IGZvciBjdXN0b21pemluZyBvciBwcm92aWRpbmcgbW9yZSBlZmZpY2llbnQgaW5pdGlhbGl6YXRpb24uCiAgICAgICAgICoKICAgICAgICAgKiBAcGFyYW0ge09iamVjdH0gcHJvcHMgQmFnIG9mIHByb3BlcnR5IHZhbHVlcyB0aGF0IHdlcmUgb3ZlcndyaXR0ZW4KICAgICAgICAgKiAgIHdoZW4gY3JlYXRpbmcgcHJvcGVydHkgYWNjZXNzb3JzLgogICAgICAgICAqIEByZXR1cm4ge3ZvaWR9CiAgICAgICAgICogQHByb3RlY3RlZAogICAgICAgICAqLwogICAgICAgIF9pbml0aWFsaXplSW5zdGFuY2VQcm9wZXJ0aWVzKHByb3BzKSB7CiAgICAgICAgICBPYmplY3QuYXNzaWduKHRoaXMsIHByb3BzKTsKICAgICAgICB9CgogICAgICAgIC8qKgogICAgICAgICAqIFVwZGF0ZXMgdGhlIGxvY2FsIHN0b3JhZ2UgZm9yIGEgcHJvcGVydHkgKHZpYSBgX3NldFBlbmRpbmdQcm9wZXJ0eWApCiAgICAgICAgICogYW5kIGVucXVldWVzIGEgYF9wcm9lcHJ0aWVzQ2hhbmdlZGAgY2FsbGJhY2suCiAgICAgICAgICoKICAgICAgICAgKiBAcGFyYW0ge3N0cmluZ30gcHJvcGVydHkgTmFtZSBvZiB0aGUgcHJvcGVydHkKICAgICAgICAgKiBAcGFyYW0geyp9IHZhbHVlIFZhbHVlIHRvIHNldAogICAgICAgICAqIEByZXR1cm4ge3ZvaWR9CiAgICAgICAgICogQHByb3RlY3RlZAogICAgICAgICAqLwogICAgICAgIF9zZXRQcm9wZXJ0eShwcm9wZXJ0eSwgdmFsdWUpIHsKICAgICAgICAgIGlmICh0aGlzLl9zZXRQZW5kaW5nUHJvcGVydHkocHJvcGVydHksIHZhbHVlKSkgewogICAgICAgICAgICB0aGlzLl9pbnZhbGlkYXRlUHJvcGVydGllcygpOwogICAgICAgICAgfQogICAgICAgIH0KCiAgICAgICAgLyoqCiAgICAgICAgICogUmV0dXJucyB0aGUgdmFsdWUgZm9yIHRoZSBnaXZlbiBwcm9wZXJ0eS4KICAgICAgICAgKiBAcGFyYW0ge3N0cmluZ30gcHJvcGVydHkgTmFtZSBvZiBwcm9wZXJ0eQogICAgICAgICAqIEByZXR1cm4geyp9IFZhbHVlIGZvciB0aGUgZ2l2ZW4gcHJvcGVydHkKICAgICAgICAgKiBAcHJvdGVjdGVkCiAgICAgICAgICovCiAgICAgICAgX2dldFByb3BlcnR5KHByb3BlcnR5KSB7CiAgICAgICAgICByZXR1cm4gdGhpcy5fX2RhdGFbcHJvcGVydHldOwogICAgICAgIH0KCiAgICAgICAgLyogZXNsaW50LWRpc2FibGUgbm8tdW51c2VkLXZhcnMgKi8KICAgICAgICAvKioKICAgICAgICAgKiBVcGRhdGVzIHRoZSBsb2NhbCBzdG9yYWdlIGZvciBhIHByb3BlcnR5LCByZWNvcmRzIHRoZSBwcmV2aW91cyB2YWx1ZSwKICAgICAgICAgKiBhbmQgYWRkcyBpdCB0byB0aGUgc2V0IG9mICJwZW5kaW5nIGNoYW5nZXMiIHRoYXQgd2lsbCBiZSBwYXNzZWQgdG8gdGhlCiAgICAgICAgICogYF9wcm9wZXJ0aWVzQ2hhbmdlZGAgY2FsbGJhY2suICBUaGlzIG1ldGhvZCBkb2VzIG5vdCBlbnF1ZXVlIHRoZQogICAgICAgICAqIGBfcHJvcGVydGllc0NoYW5nZWRgIGNhbGxiYWNrLgogICAgICAgICAqCiAgICAgICAgICogQHBhcmFtIHtzdHJpbmd9IHByb3BlcnR5IE5hbWUgb2YgdGhlIHByb3BlcnR5CiAgICAgICAgICogQHBhcmFtIHsqfSB2YWx1ZSBWYWx1ZSB0byBzZXQKICAgICAgICAgKiBAcGFyYW0ge2Jvb2xlYW49fSBleHQgTm90IHVzZWQgaGVyZTsgYWZmb3JkYW5jZSBmb3IgY2xvc3VyZQogICAgICAgICAqIEByZXR1cm4ge2Jvb2xlYW59IFJldHVybnMgdHJ1ZSBpZiB0aGUgcHJvcGVydHkgY2hhbmdlZAogICAgICAgICAqIEBwcm90ZWN0ZWQKICAgICAgICAgKi8KICAgICAgICBfc2V0UGVuZGluZ1Byb3BlcnR5KHByb3BlcnR5LCB2YWx1ZSwgZXh0KSB7CiAgICAgICAgICBsZXQgb2xkID0gdGhpcy5fX2RhdGFbcHJvcGVydHldOwogICAgICAgICAgbGV0IGNoYW5nZWQgPSB0aGlzLl9zaG91bGRQcm9wZXJ0eUNoYW5nZShwcm9wZXJ0eSwgdmFsdWUsIG9sZCk7CiAgICAgICAgICBpZiAoY2hhbmdlZCkgewogICAgICAgICAgICBpZiAoIXRoaXMuX19kYXRhUGVuZGluZykgewogICAgICAgICAgICAgIHRoaXMuX19kYXRhUGVuZGluZyA9IHt9OwogICAgICAgICAgICAgIHRoaXMuX19kYXRhT2xkID0ge307CiAgICAgICAgICAgIH0KICAgICAgICAgICAgLy8gRW5zdXJlIG9sZCBpcyBjYXB0dXJlZCBmcm9tIHRoZSBsYXN0IHR1cm4KICAgICAgICAgICAgaWYgKHRoaXMuX19kYXRhT2xkICYmICEocHJvcGVydHkgaW4gdGhpcy5fX2RhdGFPbGQpKSB7CiAgICAgICAgICAgICAgdGhpcy5fX2RhdGFPbGRbcHJvcGVydHldID0gb2xkOwogICAgICAgICAgICB9CiAgICAgICAgICAgIHRoaXMuX19kYXRhW3Byb3BlcnR5XSA9IHZhbHVlOwogICAgICAgICAgICB0aGlzLl9fZGF0YVBlbmRpbmdbcHJvcGVydHldID0gdmFsdWU7CiAgICAgICAgICB9CiAgICAgICAgICByZXR1cm4gY2hhbmdlZDsKICAgICAgICB9CiAgICAgICAgLyogZXNsaW50LWVuYWJsZSAqLwoKICAgICAgICAvKioKICAgICAgICAgKiBNYXJrcyB0aGUgcHJvcGVydGllcyBhcyBpbnZhbGlkLCBhbmQgZW5xdWV1ZXMgYW4gYXN5bmMKICAgICAgICAgKiBgX3Byb3BlcnRpZXNDaGFuZ2VkYCBjYWxsYmFjay4KICAgICAgICAgKgogICAgICAgICAqIEByZXR1cm4ge3ZvaWR9CiAgICAgICAgICogQHByb3RlY3RlZAogICAgICAgICAqLwogICAgICAgIF9pbnZhbGlkYXRlUHJvcGVydGllcygpIHsKICAgICAgICAgIGlmICghdGhpcy5fX2RhdGFJbnZhbGlkICYmIHRoaXMuX19kYXRhUmVhZHkpIHsKICAgICAgICAgICAgdGhpcy5fX2RhdGFJbnZhbGlkID0gdHJ1ZTsKICAgICAgICAgICAgbWljcm90YXNrLnJ1bigoKSA9PiB7CiAgICAgICAgICAgICAgaWYgKHRoaXMuX19kYXRhSW52YWxpZCkgewogICAgICAgICAgICAgICAgdGhpcy5fX2RhdGFJbnZhbGlkID0gZmFsc2U7CiAgICAgICAgICAgICAgICB0aGlzLl9mbHVzaFByb3BlcnRpZXMoKTsKICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0pOwogICAgICAgICAgfQogICAgICAgIH0KCiAgICAgICAgLyoqCiAgICAgICAgICogQ2FsbCB0byBlbmFibGUgcHJvcGVydHkgYWNjZXNzb3IgcHJvY2Vzc2luZy4gQmVmb3JlIHRoaXMgbWV0aG9kIGlzCiAgICAgICAgICogY2FsbGVkIGFjY2Vzc29yIHZhbHVlcyB3aWxsIGJlIHNldCBidXQgc2lkZSBlZmZlY3RzIGFyZQogICAgICAgICAqIHF1ZXVlZC4gV2hlbiBjYWxsZWQsIGFueSBwZW5kaW5nIHNpZGUgZWZmZWN0cyBvY2N1ciBpbW1lZGlhdGVseS4KICAgICAgICAgKiBGb3IgZWxlbWVudHMsIGdlbmVyYWxseSBgY29ubmVjdGVkQ2FsbGJhY2tgIGlzIGEgbm9ybWFsIHNwb3QgdG8gZG8gc28uCiAgICAgICAgICogSXQgaXMgc2FmZSB0byBjYWxsIHRoaXMgbWV0aG9kIG11bHRpcGxlIHRpbWVzIGFzIGl0IG9ubHkgdHVybnMgb24KICAgICAgICAgKiBwcm9wZXJ0eSBhY2Nlc3NvcnMgb25jZS4KICAgICAgICAgKgogICAgICAgICAqIEByZXR1cm4ge3ZvaWR9CiAgICAgICAgICogQHByb3RlY3RlZAogICAgICAgICAqLwogICAgICAgIF9lbmFibGVQcm9wZXJ0aWVzKCkgewogICAgICAgICAgaWYgKCF0aGlzLl9fZGF0YUVuYWJsZWQpIHsKICAgICAgICAgICAgdGhpcy5fX2RhdGFFbmFibGVkID0gdHJ1ZTsKICAgICAgICAgICAgaWYgKHRoaXMuX19kYXRhSW5zdGFuY2VQcm9wcykgewogICAgICAgICAgICAgIHRoaXMuX2luaXRpYWxpemVJbnN0YW5jZVByb3BlcnRpZXModGhpcy5fX2RhdGFJbnN0YW5jZVByb3BzKTsKICAgICAgICAgICAgICB0aGlzLl9fZGF0YUluc3RhbmNlUHJvcHMgPSBudWxsOwogICAgICAgICAgICB9CiAgICAgICAgICAgIHRoaXMucmVhZHkoKTsKICAgICAgICAgIH0KICAgICAgICB9CgogICAgICAgIC8qKgogICAgICAgICAqIENhbGxzIHRoZSBgX3Byb3BlcnRpZXNDaGFuZ2VkYCBjYWxsYmFjayB3aXRoIHRoZSBjdXJyZW50IHNldCBvZgogICAgICAgICAqIHBlbmRpbmcgY2hhbmdlcyAoYW5kIG9sZCB2YWx1ZXMgcmVjb3JkZWQgd2hlbiBwZW5kaW5nIGNoYW5nZXMgd2VyZQogICAgICAgICAqIHNldCksIGFuZCByZXNldHMgdGhlIHBlbmRpbmcgc2V0IG9mIGNoYW5nZXMuIEdlbmVyYWxseSwgdGhpcyBtZXRob2QKICAgICAgICAgKiBzaG91bGQgbm90IGJlIGNhbGxlZCBpbiB1c2VyIGNvZGUuCiAgICAgICAgICoKICAgICAgICAgKiBAcmV0dXJuIHt2b2lkfQogICAgICAgICAqIEBwcm90ZWN0ZWQKICAgICAgICAgKi8KICAgICAgICBfZmx1c2hQcm9wZXJ0aWVzKCkgewogICAgICAgICAgY29uc3QgcHJvcHMgPSB0aGlzLl9fZGF0YTsKICAgICAgICAgIGNvbnN0IGNoYW5nZWRQcm9wcyA9IHRoaXMuX19kYXRhUGVuZGluZzsKICAgICAgICAgIGNvbnN0IG9sZCA9IHRoaXMuX19kYXRhT2xkOwogICAgICAgICAgaWYgKHRoaXMuX3Nob3VsZFByb3BlcnRpZXNDaGFuZ2UocHJvcHMsIGNoYW5nZWRQcm9wcywgb2xkKSkgewogICAgICAgICAgICB0aGlzLl9fZGF0YVBlbmRpbmcgPSBudWxsOwogICAgICAgICAgICB0aGlzLl9fZGF0YU9sZCA9IG51bGw7CiAgICAgICAgICAgIHRoaXMuX3Byb3BlcnRpZXNDaGFuZ2VkKHByb3BzLCBjaGFuZ2VkUHJvcHMsIG9sZCk7CiAgICAgICAgICB9CiAgICAgICAgfQoKICAgICAgICAvKioKICAgICAgICAgKiBDYWxsZWQgaW4gYF9mbHVzaFByb3BlcnRpZXNgIHRvIGRldGVybWluZSBpZiBgX3Byb3BlcnRpZXNDaGFuZ2VkYAogICAgICAgICAqIHNob3VsZCBiZSBjYWxsZWQuIFRoZSBkZWZhdWx0IGltcGxlbWVudGF0aW9uIHJldHVybnMgdHJ1ZSBpZgogICAgICAgICAqIHByb3BlcnRpZXMgYXJlIHBlbmRpbmcuIE92ZXJyaWRlIHRvIGN1c3RvbWl6ZSB3aGVuCiAgICAgICAgICogYF9wcm9wZXJ0aWVzQ2hhbmdlZGAgaXMgY2FsbGVkLgogICAgICAgICAqIEBwYXJhbSB7IU9iamVjdH0gY3VycmVudFByb3BzIEJhZyBvZiBhbGwgY3VycmVudCBhY2Nlc3NvciB2YWx1ZXMKICAgICAgICAgKiBAcGFyYW0geyFPYmplY3R9IGNoYW5nZWRQcm9wcyBCYWcgb2YgcHJvcGVydGllcyBjaGFuZ2VkIHNpbmNlIHRoZSBsYXN0CiAgICAgICAgICogICBjYWxsIHRvIGBfcHJvcGVydGllc0NoYW5nZWRgCiAgICAgICAgICogQHBhcmFtIHshT2JqZWN0fSBvbGRQcm9wcyBCYWcgb2YgcHJldmlvdXMgdmFsdWVzIGZvciBlYWNoIHByb3BlcnR5CiAgICAgICAgICogICBpbiBgY2hhbmdlZFByb3BzYAogICAgICAgICAqIEByZXR1cm4ge2Jvb2xlYW59IHRydWUgaWYgY2hhbmdlZFByb3BzIGlzIHRydXRoeQogICAgICAgICAqLwogICAgICAgIF9zaG91bGRQcm9wZXJ0aWVzQ2hhbmdlKGN1cnJlbnRQcm9wcywgY2hhbmdlZFByb3BzLCBvbGRQcm9wcykgeyAvLyBlc2xpbnQtZGlzYWJsZS1saW5lIG5vLXVudXNlZC12YXJzCiAgICAgICAgICByZXR1cm4gQm9vbGVhbihjaGFuZ2VkUHJvcHMpOwogICAgICAgIH0KCiAgICAgICAgLyoqCiAgICAgICAgICogQ2FsbGJhY2sgY2FsbGVkIHdoZW4gYW55IHByb3BlcnRpZXMgd2l0aCBhY2Nlc3NvcnMgY3JlYXRlZCB2aWEKICAgICAgICAgKiBgX2NyZWF0ZVByb3BlcnR5QWNjZXNzb3JgIGhhdmUgYmVlbiBzZXQuCiAgICAgICAgICoKICAgICAgICAgKiBAcGFyYW0geyFPYmplY3R9IGN1cnJlbnRQcm9wcyBCYWcgb2YgYWxsIGN1cnJlbnQgYWNjZXNzb3IgdmFsdWVzCiAgICAgICAgICogQHBhcmFtIHshT2JqZWN0fSBjaGFuZ2VkUHJvcHMgQmFnIG9mIHByb3BlcnRpZXMgY2hhbmdlZCBzaW5jZSB0aGUgbGFzdAogICAgICAgICAqICAgY2FsbCB0byBgX3Byb3BlcnRpZXNDaGFuZ2VkYAogICAgICAgICAqIEBwYXJhbSB7IU9iamVjdH0gb2xkUHJvcHMgQmFnIG9mIHByZXZpb3VzIHZhbHVlcyBmb3IgZWFjaCBwcm9wZXJ0eQogICAgICAgICAqICAgaW4gYGNoYW5nZWRQcm9wc2AKICAgICAgICAgKiBAcmV0dXJuIHt2b2lkfQogICAgICAgICAqIEBwcm90ZWN0ZWQKICAgICAgICAgKi8KICAgICAgICBfcHJvcGVydGllc0NoYW5nZWQoY3VycmVudFByb3BzLCBjaGFuZ2VkUHJvcHMsIG9sZFByb3BzKSB7IC8vIGVzbGludC1kaXNhYmxlLWxpbmUgbm8tdW51c2VkLXZhcnMKICAgICAgICB9CgogICAgICAgIC8qKgogICAgICAgICAqIE1ldGhvZCBjYWxsZWQgdG8gZGV0ZXJtaW5lIHdoZXRoZXIgYSBwcm9wZXJ0eSB2YWx1ZSBzaG91bGQgYmUKICAgICAgICAgKiBjb25zaWRlcmVkIGFzIGEgY2hhbmdlIGFuZCBjYXVzZSB0aGUgYF9wcm9wZXJ0aWVzQ2hhbmdlZGAgY2FsbGJhY2sKICAgICAgICAgKiB0byBiZSBlbnF1ZXVlZC4KICAgICAgICAgKgogICAgICAgICAqIFRoZSBkZWZhdWx0IGltcGxlbWVudGF0aW9uIHJldHVybnMgYHRydWVgIGlmIGEgc3RyaWN0IGVxdWFsaXR5CiAgICAgICAgICogY2hlY2sgZmFpbHMuIFRoZSBtZXRob2QgYWx3YXlzIHJldHVybnMgZmFsc2UgZm9yIGBOYU5gLgogICAgICAgICAqCiAgICAgICAgICogT3ZlcnJpZGUgdGhpcyBtZXRob2QgdG8gZS5nLiBwcm92aWRlIHN0cmljdGVyIGNoZWNraW5nIGZvcgogICAgICAgICAqIE9iamVjdHMvQXJyYXlzIHdoZW4gdXNpbmcgaW1tdXRhYmxlIHBhdHRlcm5zLgogICAgICAgICAqCiAgICAgICAgICogQHBhcmFtIHtzdHJpbmd9IHByb3BlcnR5IFByb3BlcnR5IG5hbWUKICAgICAgICAgKiBAcGFyYW0geyp9IHZhbHVlIE5ldyBwcm9wZXJ0eSB2YWx1ZQogICAgICAgICAqIEBwYXJhbSB7Kn0gb2xkIFByZXZpb3VzIHByb3BlcnR5IHZhbHVlCiAgICAgICAgICogQHJldHVybiB7Ym9vbGVhbn0gV2hldGhlciB0aGUgcHJvcGVydHkgc2hvdWxkIGJlIGNvbnNpZGVyZWQgYSBjaGFuZ2UKICAgICAgICAgKiAgIGFuZCBlbnF1ZXVlIGEgYF9wcm9lcHJ0aWVzQ2hhbmdlZGAgY2FsbGJhY2sKICAgICAgICAgKiBAcHJvdGVjdGVkCiAgICAgICAgICovCiAgICAgICAgX3Nob3VsZFByb3BlcnR5Q2hhbmdlKHByb3BlcnR5LCB2YWx1ZSwgb2xkKSB7CiAgICAgICAgICByZXR1cm4gKAogICAgICAgICAgICAvLyBTdHJpY3QgZXF1YWxpdHkgY2hlY2sKICAgICAgICAgICAgKG9sZCAhPT0gdmFsdWUgJiYKICAgICAgICAgICAgICAvLyBUaGlzIGVuc3VyZXMgKG9sZD09TmFOLCB2YWx1ZT09TmFOKSBhbHdheXMgcmV0dXJucyBmYWxzZQogICAgICAgICAgICAgIChvbGQgPT09IG9sZCB8fCB2YWx1ZSA9PT0gdmFsdWUpKQogICAgICAgICAgKTsKICAgICAgICB9CgogICAgICAgIC8qKgogICAgICAgICAqIEltcGxlbWVudHMgbmF0aXZlIEN1c3RvbSBFbGVtZW50cyBgYXR0cmlidXRlQ2hhbmdlZENhbGxiYWNrYCB0bwogICAgICAgICAqIHNldCBhbiBhdHRyaWJ1dGUgdmFsdWUgdG8gYSBwcm9wZXJ0eSB2aWEgYF9hdHRyaWJ1dGVUb1Byb3BlcnR5YC4KICAgICAgICAgKgogICAgICAgICAqIEBwYXJhbSB7c3RyaW5nfSBuYW1lIE5hbWUgb2YgYXR0cmlidXRlIHRoYXQgY2hhbmdlZAogICAgICAgICAqIEBwYXJhbSB7P3N0cmluZ30gb2xkIE9sZCBhdHRyaWJ1dGUgdmFsdWUKICAgICAgICAgKiBAcGFyYW0gez9zdHJpbmd9IHZhbHVlIE5ldyBhdHRyaWJ1dGUgdmFsdWUKICAgICAgICAgKiBAcGFyYW0gez9zdHJpbmd9IG5hbWVzcGFjZSBBdHRyaWJ1dGUgbmFtZXNwYWNlLgogICAgICAgICAqIEByZXR1cm4ge3ZvaWR9CiAgICAgICAgICogQHN1cHByZXNzIHttaXNzaW5nUHJvcGVydGllc30gU3VwZXIgbWF5IG9yIG1heSBub3QgaW1wbGVtZW50IHRoZSBjYWxsYmFjawogICAgICAgICAqLwogICAgICAgIGF0dHJpYnV0ZUNoYW5nZWRDYWxsYmFjayhuYW1lLCBvbGQsIHZhbHVlLCBuYW1lc3BhY2UpIHsKICAgICAgICAgIGlmIChvbGQgIT09IHZhbHVlKSB7CiAgICAgICAgICAgIHRoaXMuX2F0dHJpYnV0ZVRvUHJvcGVydHkobmFtZSwgdmFsdWUpOwogICAgICAgICAgfQogICAgICAgICAgaWYgKHN1cGVyLmF0dHJpYnV0ZUNoYW5nZWRDYWxsYmFjaykgewogICAgICAgICAgICBzdXBlci5hdHRyaWJ1dGVDaGFuZ2VkQ2FsbGJhY2sobmFtZSwgb2xkLCB2YWx1ZSwgbmFtZXNwYWNlKTsKICAgICAgICAgIH0KICAgICAgICB9CgogICAgICAgIC8qKgogICAgICAgICAqIERlc2VyaWFsaXplcyBhbiBhdHRyaWJ1dGUgdG8gaXRzIGFzc29jaWF0ZWQgcHJvcGVydHkuCiAgICAgICAgICoKICAgICAgICAgKiBUaGlzIG1ldGhvZCBjYWxscyB0aGUgYF9kZXNlcmlhbGl6ZVZhbHVlYCBtZXRob2QgdG8gY29udmVydCB0aGUgc3RyaW5nIHRvCiAgICAgICAgICogYSB0eXBlZCB2YWx1ZS4KICAgICAgICAgKgogICAgICAgICAqIEBwYXJhbSB7c3RyaW5nfSBhdHRyaWJ1dGUgTmFtZSBvZiBhdHRyaWJ1dGUgdG8gZGVzZXJpYWxpemUuCiAgICAgICAgICogQHBhcmFtIHs/c3RyaW5nfSB2YWx1ZSBvZiB0aGUgYXR0cmlidXRlLgogICAgICAgICAqIEBwYXJhbSB7Kj19IHR5cGUgdHlwZSB0byBkZXNlcmlhbGl6ZSB0bywgZGVmYXVsdHMgdG8gdGhlIHZhbHVlCiAgICAgICAgICogcmV0dXJuZWQgZnJvbSBgdHlwZUZvclByb3BlcnR5YAogICAgICAgICAqIEByZXR1cm4ge3ZvaWR9CiAgICAgICAgICovCiAgICAgICAgX2F0dHJpYnV0ZVRvUHJvcGVydHkoYXR0cmlidXRlLCB2YWx1ZSwgdHlwZSkgewogICAgICAgICAgaWYgKCF0aGlzLl9fc2VyaWFsaXppbmcpIHsKICAgICAgICAgICAgY29uc3QgbWFwID0gdGhpcy5fX2RhdGFBdHRyaWJ1dGVzOwogICAgICAgICAgICBjb25zdCBwcm9wZXJ0eSA9IG1hcCAmJiBtYXBbYXR0cmlidXRlXSB8fCBhdHRyaWJ1dGU7CiAgICAgICAgICAgIHRoaXNbcHJvcGVydHldID0gdGhpcy5fZGVzZXJpYWxpemVWYWx1ZSh2YWx1ZSwgdHlwZSB8fAogICAgICAgICAgICAgIHRoaXMuY29uc3RydWN0b3IudHlwZUZvclByb3BlcnR5KHByb3BlcnR5KSk7CiAgICAgICAgICB9CiAgICAgICAgfQoKICAgICAgICAvKioKICAgICAgICAgKiBTZXJpYWxpemVzIGEgcHJvcGVydHkgdG8gaXRzIGFzc29jaWF0ZWQgYXR0cmlidXRlLgogICAgICAgICAqCiAgICAgICAgICogQHN1cHByZXNzIHtpbnZhbGlkQ2FzdHN9IENsb3N1cmUgY2FuJ3QgZmlndXJlIG91dCBgdGhpc2AgaXMgYW4gZWxlbWVudC4KICAgICAgICAgKgogICAgICAgICAqIEBwYXJhbSB7c3RyaW5nfSBwcm9wZXJ0eSBQcm9wZXJ0eSBuYW1lIHRvIHJlZmxlY3QuCiAgICAgICAgICogQHBhcmFtIHtzdHJpbmc9fSBhdHRyaWJ1dGUgQXR0cmlidXRlIG5hbWUgdG8gcmVmbGVjdCB0by4KICAgICAgICAgKiBAcGFyYW0geyo9fSB2YWx1ZSBQcm9wZXJ0eSB2YWx1ZSB0byByZWZlY3QuCiAgICAgICAgICogQHJldHVybiB7dm9pZH0KICAgICAgICAgKi8KICAgICAgICBfcHJvcGVydHlUb0F0dHJpYnV0ZShwcm9wZXJ0eSwgYXR0cmlidXRlLCB2YWx1ZSkgewogICAgICAgICAgdGhpcy5fX3NlcmlhbGl6aW5nID0gdHJ1ZTsKICAgICAgICAgIHZhbHVlID0gKGFyZ3VtZW50cy5sZW5ndGggPCAzKSA/IHRoaXNbcHJvcGVydHldIDogdmFsdWU7CiAgICAgICAgICB0aGlzLl92YWx1ZVRvTm9kZUF0dHJpYnV0ZSgvKiogQHR5cGUgeyFIVE1MRWxlbWVudH0gKi8odGhpcyksIHZhbHVlLAogICAgICAgICAgICBhdHRyaWJ1dGUgfHwgdGhpcy5jb25zdHJ1Y3Rvci5hdHRyaWJ1dGVOYW1lRm9yUHJvcGVydHkocHJvcGVydHkpKTsKICAgICAgICAgIHRoaXMuX19zZXJpYWxpemluZyA9IGZhbHNlOwogICAgICAgIH0KCiAgICAgICAgLyoqCiAgICAgICAgICogU2V0cyBhIHR5cGVkIHZhbHVlIHRvIGFuIEhUTUwgYXR0cmlidXRlIG9uIGEgbm9kZS4KICAgICAgICAgKgogICAgICAgICAqIFRoaXMgbWV0aG9kIGNhbGxzIHRoZSBgX3NlcmlhbGl6ZVZhbHVlYCBtZXRob2QgdG8gY29udmVydCB0aGUgdHlwZWQKICAgICAgICAgKiB2YWx1ZSB0byBhIHN0cmluZy4gIElmIHRoZSBgX3NlcmlhbGl6ZVZhbHVlYCBtZXRob2QgcmV0dXJucyBgdW5kZWZpbmVkYCwKICAgICAgICAgKiB0aGUgYXR0cmlidXRlIHdpbGwgYmUgcmVtb3ZlZCAodGhpcyBpcyB0aGUgZGVmYXVsdCBmb3IgYm9vbGVhbgogICAgICAgICAqIHR5cGUgYGZhbHNlYCkuCiAgICAgICAgICoKICAgICAgICAgKiBAcGFyYW0ge0VsZW1lbnR9IG5vZGUgRWxlbWVudCB0byBzZXQgYXR0cmlidXRlIHRvLgogICAgICAgICAqIEBwYXJhbSB7Kn0gdmFsdWUgVmFsdWUgdG8gc2VyaWFsaXplLgogICAgICAgICAqIEBwYXJhbSB7c3RyaW5nfSBhdHRyaWJ1dGUgQXR0cmlidXRlIG5hbWUgdG8gc2VyaWFsaXplIHRvLgogICAgICAgICAqIEByZXR1cm4ge3ZvaWR9CiAgICAgICAgICovCiAgICAgICAgX3ZhbHVlVG9Ob2RlQXR0cmlidXRlKG5vZGUsIHZhbHVlLCBhdHRyaWJ1dGUpIHsKICAgICAgICAgIGNvbnN0IHN0ciA9IHRoaXMuX3NlcmlhbGl6ZVZhbHVlKHZhbHVlKTsKICAgICAgICAgIGlmIChzdHIgPT09IHVuZGVmaW5lZCkgewogICAgICAgICAgICBub2RlLnJlbW92ZUF0dHJpYnV0ZShhdHRyaWJ1dGUpOwogICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgbm9kZS5zZXRBdHRyaWJ1dGUoYXR0cmlidXRlLCBzdHIpOwogICAgICAgICAgfQogICAgICAgIH0KCiAgICAgICAgLyoqCiAgICAgICAgICogQ29udmVydHMgYSB0eXBlZCBKYXZhU2NyaXB0IHZhbHVlIHRvIGEgc3RyaW5nLgogICAgICAgICAqCiAgICAgICAgICogVGhpcyBtZXRob2QgaXMgY2FsbGVkIHdoZW4gc2V0dGluZyBKUyBwcm9wZXJ0eSB2YWx1ZXMgdG8KICAgICAgICAgKiBIVE1MIGF0dHJpYnV0ZXMuICBVc2VycyBtYXkgb3ZlcnJpZGUgdGhpcyBtZXRob2QgdG8gcHJvdmlkZQogICAgICAgICAqIHNlcmlhbGl6YXRpb24gZm9yIGN1c3RvbSB0eXBlcy4KICAgICAgICAgKgogICAgICAgICAqIEBwYXJhbSB7Kn0gdmFsdWUgUHJvcGVydHkgdmFsdWUgdG8gc2VyaWFsaXplLgogICAgICAgICAqIEByZXR1cm4ge3N0cmluZyB8IHVuZGVmaW5lZH0gU3RyaW5nIHNlcmlhbGl6ZWQgZnJvbSB0aGUgcHJvdmlkZWQKICAgICAgICAgKiBwcm9wZXJ0eSAgdmFsdWUuCiAgICAgICAgICovCiAgICAgICAgX3NlcmlhbGl6ZVZhbHVlKHZhbHVlKSB7CiAgICAgICAgICBzd2l0Y2ggKHR5cGVvZiB2YWx1ZSkgewogICAgICAgICAgICBjYXNlICdib29sZWFuJzoKICAgICAgICAgICAgICByZXR1cm4gdmFsdWUgPyAnJyA6IHVuZGVmaW5lZDsKICAgICAgICAgICAgZGVmYXVsdDoKICAgICAgICAgICAgICByZXR1cm4gdmFsdWUgIT0gbnVsbCA/IHZhbHVlLnRvU3RyaW5nKCkgOiB1bmRlZmluZWQ7CiAgICAgICAgICB9CiAgICAgICAgfQoKICAgICAgICAvKioKICAgICAgICAgKiBDb252ZXJ0cyBhIHN0cmluZyB0byBhIHR5cGVkIEphdmFTY3JpcHQgdmFsdWUuCiAgICAgICAgICoKICAgICAgICAgKiBUaGlzIG1ldGhvZCBpcyBjYWxsZWQgd2hlbiByZWFkaW5nIEhUTUwgYXR0cmlidXRlIHZhbHVlcyB0bwogICAgICAgICAqIEpTIHByb3BlcnRpZXMuICBVc2VycyBtYXkgb3ZlcnJpZGUgdGhpcyBtZXRob2QgdG8gcHJvdmlkZQogICAgICAgICAqIGRlc2VyaWFsaXphdGlvbiBmb3IgY3VzdG9tIGB0eXBlYHMuIFR5cGVzIGZvciBgQm9vbGVhbmAsIGBTdHJpbmdgLAogICAgICAgICAqIGFuZCBgTnVtYmVyYCBjb252ZXJ0IGF0dHJpYnV0ZXMgdG8gdGhlIGV4cGVjdGVkIHR5cGVzLgogICAgICAgICAqCiAgICAgICAgICogQHBhcmFtIHs/c3RyaW5nfSB2YWx1ZSBWYWx1ZSB0byBkZXNlcmlhbGl6ZS4KICAgICAgICAgKiBAcGFyYW0geyo9fSB0eXBlIFR5cGUgdG8gZGVzZXJpYWxpemUgdGhlIHN0cmluZyB0by4KICAgICAgICAgKiBAcmV0dXJuIHsqfSBUeXBlZCB2YWx1ZSBkZXNlcmlhbGl6ZWQgZnJvbSB0aGUgcHJvdmlkZWQgc3RyaW5nLgogICAgICAgICAqLwogICAgICAgIF9kZXNlcmlhbGl6ZVZhbHVlKHZhbHVlLCB0eXBlKSB7CiAgICAgICAgICBzd2l0Y2ggKHR5cGUpIHsKICAgICAgICAgICAgY2FzZSBCb29sZWFuOgogICAgICAgICAgICAgIHJldHVybiAodmFsdWUgIT09IG51bGwpOwogICAgICAgICAgICBjYXNlIE51bWJlcjoKICAgICAgICAgICAgICByZXR1cm4gTnVtYmVyKHZhbHVlKTsKICAgICAgICAgICAgZGVmYXVsdDoKICAgICAgICAgICAgICByZXR1cm4gdmFsdWU7CiAgICAgICAgICB9CiAgICAgICAgfQoKICAgICAgfQoKICAgICAgcmV0dXJuIFByb3BlcnRpZXNDaGFuZ2VkOwogICAgfSk7CgoKICB9KSgpOwoKPC9zY3JpcHQ+PHNjcmlwdD4KKGZ1bmN0aW9uKCkgewoKICAndXNlIHN0cmljdCc7CgogIGxldCBjYXNlTWFwID0gUG9seW1lci5DYXNlTWFwOwoKICAvLyBTYXZlIG1hcCBvZiBuYXRpdmUgcHJvcGVydGllczsgdGhpcyBmb3JtcyBhIGJsYWNrbGlzdCBvciBwcm9wZXJ0aWVzCiAgLy8gdGhhdCB3b24ndCBoYXZlIHRoZWlyIHZhbHVlcyAic2F2ZWQiIGJ5IGBzYXZlQWNjZXNzb3JWYWx1ZWAsIHNpbmNlCiAgLy8gcmVhZGluZyBmcm9tIGFuIEhUTUxFbGVtZW50IGFjY2Vzc29yIGZyb20gdGhlIGNvbnRleHQgb2YgYSBwcm90b3R5cGUgdGhyb3dzCiAgY29uc3QgbmF0aXZlUHJvcGVydGllcyA9IHt9OwogIGxldCBwcm90byA9IEhUTUxFbGVtZW50LnByb3RvdHlwZTsKICB3aGlsZSAocHJvdG8pIHsKICAgIGxldCBwcm9wcyA9IE9iamVjdC5nZXRPd25Qcm9wZXJ0eU5hbWVzKHByb3RvKTsKICAgIGZvciAobGV0IGk9MDsgaTxwcm9wcy5sZW5ndGg7IGkrKykgewogICAgICBuYXRpdmVQcm9wZXJ0aWVzW3Byb3BzW2ldXSA9IHRydWU7CiAgICB9CiAgICBwcm90byA9IE9iamVjdC5nZXRQcm90b3R5cGVPZihwcm90byk7CiAgfQoKICAvKioKICAgKiBVc2VkIHRvIHNhdmUgdGhlIHZhbHVlIG9mIGEgcHJvcGVydHkgdGhhdCB3aWxsIGJlIG92ZXJyaWRkZW4gd2l0aAogICAqIGFuIGFjY2Vzc29yLiBJZiB0aGUgYG1vZGVsYCBpcyBhIHByb3RvdHlwZSwgdGhlIHZhbHVlcyB3aWxsIGJlIHNhdmVkCiAgICogaW4gYF9fZGF0YVByb3RvYCwgYW5kIGl0J3MgdXAgdG8gdGhlIHVzZXIgKG9yIGRvd25zdHJlYW0gbWl4aW4pIHRvCiAgICogZGVjaWRlIGhvdy93aGVuIHRvIHNldCB0aGVzZSB2YWx1ZXMgYmFjayBpbnRvIHRoZSBhY2Nlc3NvcnMuCiAgICogSWYgYG1vZGVsYCBpcyBhbHJlYWR5IGFuIGluc3RhbmNlIChpdCBoYXMgYSBgX19kYXRhYCBwcm9wZXJ0eSksIHRoZW4KICAgKiB0aGUgdmFsdWUgd2lsbCBiZSBzZXQgYXMgYSBwZW5kaW5nIHByb3BlcnR5LCBtZWFuaW5nIHRoZSB1c2VyIHNob3VsZAogICAqIGNhbGwgYF9pbnZhbGlkYXRlUHJvcGVydGllc2Agb3IgYF9mbHVzaFByb3BlcnRpZXNgIHRvIHRha2UgZWZmZWN0CiAgICoKICAgKiBAcGFyYW0ge09iamVjdH0gbW9kZWwgUHJvdG90eXBlIG9yIGluc3RhbmNlCiAgICogQHBhcmFtIHtzdHJpbmd9IHByb3BlcnR5IE5hbWUgb2YgcHJvcGVydHkKICAgKiBAcmV0dXJuIHt2b2lkfQogICAqIEBwcml2YXRlCiAgICovCiAgZnVuY3Rpb24gc2F2ZUFjY2Vzc29yVmFsdWUobW9kZWwsIHByb3BlcnR5KSB7CiAgICAvLyBEb24ndCByZWFkL3N0b3JlIHZhbHVlIGZvciBhbnkgbmF0aXZlIHByb3BlcnRpZXMgc2luY2UgdGhleSBjb3VsZCB0aHJvdwogICAgaWYgKCFuYXRpdmVQcm9wZXJ0aWVzW3Byb3BlcnR5XSkgewogICAgICBsZXQgdmFsdWUgPSBtb2RlbFtwcm9wZXJ0eV07CiAgICAgIGlmICh2YWx1ZSAhPT0gdW5kZWZpbmVkKSB7CiAgICAgICAgaWYgKG1vZGVsLl9fZGF0YSkgewogICAgICAgICAgLy8gQWRkaW5nIGFjY2Vzc29yIHRvIGluc3RhbmNlOyB1cGRhdGUgdGhlIHByb3BlcnR5CiAgICAgICAgICAvLyBJdCBpcyB0aGUgdXNlcidzIHJlc3BvbnNpYmlsaXR5IHRvIGNhbGwgX2ZsdXNoUHJvcGVydGllcwogICAgICAgICAgbW9kZWwuX3NldFBlbmRpbmdQcm9wZXJ0eShwcm9wZXJ0eSwgdmFsdWUpOwogICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAvLyBBZGRpbmcgYWNjZXNzb3IgdG8gcHJvdG87IHNhdmUgcHJvdG8ncyB2YWx1ZSBmb3IgaW5zdGFuY2UtdGltZSB1c2UKICAgICAgICAgIGlmICghbW9kZWwuX19kYXRhUHJvdG8pIHsKICAgICAgICAgICAgbW9kZWwuX19kYXRhUHJvdG8gPSB7fTsKICAgICAgICAgIH0gZWxzZSBpZiAoIW1vZGVsLmhhc093blByb3BlcnR5KEpTQ29tcGlsZXJfcmVuYW1lUHJvcGVydHkoJ19fZGF0YVByb3RvJywgbW9kZWwpKSkgewogICAgICAgICAgICBtb2RlbC5fX2RhdGFQcm90byA9IE9iamVjdC5jcmVhdGUobW9kZWwuX19kYXRhUHJvdG8pOwogICAgICAgICAgfQogICAgICAgICAgbW9kZWwuX19kYXRhUHJvdG9bcHJvcGVydHldID0gdmFsdWU7CiAgICAgICAgfQogICAgICB9CiAgICB9CiAgfQoKICAvKioKICAgKiBFbGVtZW50IGNsYXNzIG1peGluIHRoYXQgcHJvdmlkZXMgYmFzaWMgbWV0YS1wcm9ncmFtbWluZyBmb3IgY3JlYXRpbmcgb25lCiAgICogb3IgbW9yZSBwcm9wZXJ0eSBhY2Nlc3NvcnMgKGdldHRlci9zZXR0ZXIgcGFpcikgdGhhdCBlbnF1ZXVlIGFuIGFzeW5jCiAgICogKGJhdGNoZWQpIGBfcHJvcGVydGllc0NoYW5nZWRgIGNhbGxiYWNrLgogICAqCiAgICogRm9yIGJhc2ljIHVzYWdlIG9mIHRoaXMgbWl4aW46CiAgICogCiAgICogLSAgIERlY2xhcmUgYXR0cmlidXRlcyB0byBvYnNlcnZlIHZpYSB0aGUgc3RhbmRhcmQgYHN0YXRpYyBnZXQgb2JzZXJ2ZWRBdHRyaWJ1dGVzKClgLiBVc2UKICAgKiAgICAgYGRhc2gtY2FzZWAgYXR0cmlidXRlIG5hbWVzIHRvIHJlcHJlc2VudCBgY2FtZWxDYXNlYCBwcm9wZXJ0eSBuYW1lcy4gCiAgICogLSAgIEltcGxlbWVudCB0aGUgYF9wcm9wZXJ0aWVzQ2hhbmdlZGAgY2FsbGJhY2sgb24gdGhlIGNsYXNzLgogICAqIC0gICBDYWxsIGBNeUNsYXNzLmNyZWF0ZVByb3BlcnRpZXNGb3JBdHRyaWJ1dGVzKClgICoqb25jZSoqIG9uIHRoZSBjbGFzcyB0byBnZW5lcmF0ZSAKICAgKiAgICAgcHJvcGVydHkgYWNjZXNzb3JzIGZvciBlYWNoIG9ic2VydmVkIGF0dHJpYnV0ZS4gVGhpcyBtdXN0IGJlIGNhbGxlZCBiZWZvcmUgdGhlIGZpcnN0IAogICAqICAgICBpbnN0YW5jZSBpcyBjcmVhdGVkLCBmb3IgZXhhbXBsZSwgYnkgY2FsbGluZyBpdCBiZWZvcmUgY2FsbGluZyBgY3VzdG9tRWxlbWVudHMuZGVmaW5lYC4KICAgKiAgICAgSXQgY2FuIGFsc28gYmUgY2FsbGVkIGxhemlseSBmcm9tIHRoZSBlbGVtZW50J3MgYGNvbnN0cnVjdG9yYCwgYXMgbG9uZyBhcyBpdCdzIGd1YXJkZWQgc28KICAgKiAgICAgdGhhdCB0aGUgY2FsbCBpcyBvbmx5IG1hZGUgb25jZSwgd2hlbiB0aGUgZmlyc3QgaW5zdGFuY2UgaXMgY3JlYXRlZC4KICAgKiAtICAgQ2FsbCBgdGhpcy5fZW5hYmxlUHJvcGVydGllcygpYCBpbiB0aGUgZWxlbWVudCdzIGBjb25uZWN0ZWRDYWxsYmFja2AgdG8gZW5hYmxlIAogICAqICAgICB0aGUgYWNjZXNzb3JzLgogICAqCiAgICogQW55IGBvYnNlcnZlZEF0dHJpYnV0ZXNgIHdpbGwgYXV0b21hdGljYWxseSBiZQogICAqIGRlc2VyaWFsaXplZCB2aWEgYGF0dHJpYnV0ZUNoYW5nZWRDYWxsYmFja2AgYW5kIHNldCB0byB0aGUgYXNzb2NpYXRlZAogICAqIHByb3BlcnR5IHVzaW5nIGBkYXNoLWNhc2VgLXRvLWBjYW1lbENhc2VgIGNvbnZlbnRpb24uCiAgICoKICAgKiBAbWl4aW5GdW5jdGlvbgogICAqIEBwb2x5bWVyCiAgICogQGFwcGxpZXNNaXhpbiBQb2x5bWVyLlByb3BlcnRpZXNDaGFuZ2VkCiAgICogQG1lbWJlcm9mIFBvbHltZXIKICAgKiBAc3VtbWFyeSBFbGVtZW50IGNsYXNzIG1peGluIGZvciByZWFjdGluZyB0byBwcm9wZXJ0eSBjaGFuZ2VzIGZyb20KICAgKiAgIGdlbmVyYXRlZCBwcm9wZXJ0eSBhY2Nlc3NvcnMuCiAgICovCiAgUG9seW1lci5Qcm9wZXJ0eUFjY2Vzc29ycyA9IFBvbHltZXIuZGVkdXBpbmdNaXhpbihzdXBlckNsYXNzID0+IHsKCiAgICAvKioKICAgICAqIEBjb25zdHJ1Y3RvcgogICAgICogQGV4dGVuZHMge3N1cGVyQ2xhc3N9CiAgICAgKiBAaW1wbGVtZW50cyB7UG9seW1lcl9Qcm9wZXJ0aWVzQ2hhbmdlZH0KICAgICAqIEB1bnJlc3RyaWN0ZWQKICAgICAqIEBwcml2YXRlCiAgICAgKi8KICAgICBjb25zdCBiYXNlID0gUG9seW1lci5Qcm9wZXJ0aWVzQ2hhbmdlZChzdXBlckNsYXNzKTsKCiAgICAvKioKICAgICAqIEBwb2x5bWVyCiAgICAgKiBAbWl4aW5DbGFzcwogICAgICogQGltcGxlbWVudHMge1BvbHltZXJfUHJvcGVydHlBY2Nlc3NvcnN9CiAgICAgKiBAZXh0ZW5kcyB7YmFzZX0KICAgICAqIEB1bnJlc3RyaWN0ZWQKICAgICAqLwogICAgY2xhc3MgUHJvcGVydHlBY2Nlc3NvcnMgZXh0ZW5kcyBiYXNlIHsKCiAgICAgIC8qKgogICAgICAgKiBHZW5lcmF0ZXMgcHJvcGVydHkgYWNjZXNzb3JzIGZvciBhbGwgYXR0cmlidXRlcyBpbiB0aGUgc3RhbmRhcmQKICAgICAgICogc3RhdGljIGBvYnNlcnZlZEF0dHJpYnV0ZXNgIGFycmF5LgogICAgICAgKgogICAgICAgKiBBdHRyaWJ1dGUgbmFtZXMgYXJlIG1hcHBlZCB0byBwcm9wZXJ0eSBuYW1lcyB1c2luZyB0aGUgYGRhc2gtY2FzZWAgdG8KICAgICAgICogYGNhbWVsQ2FzZWAgY29udmVudGlvbgogICAgICAgKgogICAgICAgKiBAcmV0dXJuIHt2b2lkfQogICAgICAgKi8KICAgICAgc3RhdGljIGNyZWF0ZVByb3BlcnRpZXNGb3JBdHRyaWJ1dGVzKCkgewogICAgICAgIGxldCBhJCA9IHRoaXMub2JzZXJ2ZWRBdHRyaWJ1dGVzOwogICAgICAgIGZvciAobGV0IGk9MDsgaSA8IGEkLmxlbmd0aDsgaSsrKSB7CiAgICAgICAgICB0aGlzLnByb3RvdHlwZS5fY3JlYXRlUHJvcGVydHlBY2Nlc3NvcihjYXNlTWFwLmRhc2hUb0NhbWVsQ2FzZShhJFtpXSkpOwogICAgICAgIH0KICAgICAgfQoKICAgICAgLyoqCiAgICAgICAqIFJldHVybnMgYW4gYXR0cmlidXRlIG5hbWUgdGhhdCBjb3JyZXNwb25kcyB0byB0aGUgZ2l2ZW4gcHJvcGVydHkuCiAgICAgICAqIEJ5IGRlZmF1bHQsIGNvbnZlcnRzIGNhbWVsIHRvIGRhc2ggY2FzZSwgZS5nLiBgZm9vQmFyYCB0byBgZm9vLWJhcmAuCiAgICAgICAqIEBwYXJhbSB7c3RyaW5nfSBwcm9wZXJ0eSBQcm9wZXJ0eSB0byBjb252ZXJ0CiAgICAgICAqIEByZXR1cm4ge3N0cmluZ30gQXR0cmlidXRlIG5hbWUgY29ycmVzcG9uZGluZyB0byB0aGUgZ2l2ZW4gcHJvcGVydHkuCiAgICAgICAqCiAgICAgICAqIEBwcm90ZWN0ZWQKICAgICAgICovCiAgICAgIHN0YXRpYyBhdHRyaWJ1dGVOYW1lRm9yUHJvcGVydHkocHJvcGVydHkpIHsKICAgICAgICByZXR1cm4gY2FzZU1hcC5jYW1lbFRvRGFzaENhc2UocHJvcGVydHkpOwogICAgICB9CgogICAgICAvKioKICAgICAgICogT3ZlcnJpZGVzIFByb3BlcnRpZXNDaGFuZ2VkIGltcGxlbWVudGF0aW9uIHRvIGluaXRpYWxpemUgdmFsdWVzIGZvcgogICAgICAgKiBhY2Nlc3NvcnMgY3JlYXRlZCBmb3IgdmFsdWVzIHRoYXQgYWxyZWFkeSBleGlzdGVkIG9uIHRoZSBlbGVtZW50CiAgICAgICAqIHByb3RvdHlwZS4KICAgICAgICoKICAgICAgICogQHJldHVybiB7dm9pZH0KICAgICAgICogQHByb3RlY3RlZAogICAgICAgKi8KICAgICAgX2luaXRpYWxpemVQcm9wZXJ0aWVzKCkgewogICAgICAgIGlmICh0aGlzLl9fZGF0YVByb3RvKSB7CiAgICAgICAgICB0aGlzLl9pbml0aWFsaXplUHJvdG9Qcm9wZXJ0aWVzKHRoaXMuX19kYXRhUHJvdG8pOwogICAgICAgICAgdGhpcy5fX2RhdGFQcm90byA9IG51bGw7CiAgICAgICAgfQogICAgICAgIHN1cGVyLl9pbml0aWFsaXplUHJvcGVydGllcygpOwogICAgICB9CgogICAgICAvKioKICAgICAgICogQ2FsbGVkIGF0IGluc3RhbmNlIHRpbWUgd2l0aCBiYWcgb2YgcHJvcGVydGllcyB0aGF0IHdlcmUgb3ZlcndyaXR0ZW4KICAgICAgICogYnkgYWNjZXNzb3JzIG9uIHRoZSBwcm90b3R5cGUgd2hlbiBhY2Nlc3NvcnMgd2VyZSBjcmVhdGVkLgogICAgICAgKgogICAgICAgKiBUaGUgZGVmYXVsdCBpbXBsZW1lbnRhdGlvbiBzZXRzIHRoZXNlIHByb3BlcnRpZXMgYmFjayBpbnRvIHRoZQogICAgICAgKiBzZXR0ZXIgYXQgaW5zdGFuY2UgdGltZS4gIFRoaXMgbWV0aG9kIGlzIHByb3ZpZGVkIGFzIGFuIG92ZXJyaWRlCiAgICAgICAqIHBvaW50IGZvciBjdXN0b21pemluZyBvciBwcm92aWRpbmcgbW9yZSBlZmZpY2llbnQgaW5pdGlhbGl6YXRpb24uCiAgICAgICAqCiAgICAgICAqIEBwYXJhbSB7T2JqZWN0fSBwcm9wcyBCYWcgb2YgcHJvcGVydHkgdmFsdWVzIHRoYXQgd2VyZSBvdmVyd3JpdHRlbgogICAgICAgKiAgIHdoZW4gY3JlYXRpbmcgcHJvcGVydHkgYWNjZXNzb3JzLgogICAgICAgKiBAcmV0dXJuIHt2b2lkfQogICAgICAgKiBAcHJvdGVjdGVkCiAgICAgICAqLwogICAgICBfaW5pdGlhbGl6ZVByb3RvUHJvcGVydGllcyhwcm9wcykgewogICAgICAgIGZvciAobGV0IHAgaW4gcHJvcHMpIHsKICAgICAgICAgIHRoaXMuX3NldFByb3BlcnR5KHAsIHByb3BzW3BdKTsKICAgICAgICB9CiAgICAgIH0KCiAgICAgIC8qKgogICAgICAgKiBFbnN1cmVzIHRoZSBlbGVtZW50IGhhcyB0aGUgZ2l2ZW4gYXR0cmlidXRlLiBJZiBpdCBkb2VzIG5vdCwKICAgICAgICogYXNzaWducyB0aGUgZ2l2ZW4gdmFsdWUgdG8gdGhlIGF0dHJpYnV0ZS4KICAgICAgICoKICAgICAgICogQHN1cHByZXNzIHtpbnZhbGlkQ2FzdHN9IENsb3N1cmUgY2FuJ3QgZmlndXJlIG91dCBgdGhpc2AgaXMgaW5mYWN0IGFuIGVsZW1lbnQKICAgICAgICoKICAgICAgICogQHBhcmFtIHtzdHJpbmd9IGF0dHJpYnV0ZSBOYW1lIG9mIGF0dHJpYnV0ZSB0byBlbnN1cmUgaXMgc2V0LgogICAgICAgKiBAcGFyYW0ge3N0cmluZ30gdmFsdWUgb2YgdGhlIGF0dHJpYnV0ZS4KICAgICAgICogQHJldHVybiB7dm9pZH0KICAgICAgICovCiAgICAgIF9lbnN1cmVBdHRyaWJ1dGUoYXR0cmlidXRlLCB2YWx1ZSkgewogICAgICAgIGNvbnN0IGVsID0gLyoqIEB0eXBlIHshSFRNTEVsZW1lbnR9ICovKHRoaXMpOwogICAgICAgIGlmICghZWwuaGFzQXR0cmlidXRlKGF0dHJpYnV0ZSkpIHsKICAgICAgICAgIHRoaXMuX3ZhbHVlVG9Ob2RlQXR0cmlidXRlKGVsLCB2YWx1ZSwgYXR0cmlidXRlKTsKICAgICAgICB9CiAgICAgIH0KCiAgICAgIC8qKgogICAgICAgKiBPdmVycmlkZXMgUHJvcGVydGllc0NoYW5nZWQgaW1wbGVtZW50aW9uIHRvIHNlcmlhbGl6ZSBvYmplY3RzIGFzIEpTT04uCiAgICAgICAqCiAgICAgICAqIEBwYXJhbSB7Kn0gdmFsdWUgUHJvcGVydHkgdmFsdWUgdG8gc2VyaWFsaXplLgogICAgICAgKiBAcmV0dXJuIHtzdHJpbmcgfCB1bmRlZmluZWR9IFN0cmluZyBzZXJpYWxpemVkIGZyb20gdGhlIHByb3ZpZGVkIHByb3BlcnR5IHZhbHVlLgogICAgICAgKi8KICAgICAgX3NlcmlhbGl6ZVZhbHVlKHZhbHVlKSB7CiAgICAgICAgLyogZXNsaW50LWRpc2FibGUgbm8tZmFsbHRocm91Z2ggKi8KICAgICAgICBzd2l0Y2ggKHR5cGVvZiB2YWx1ZSkgewogICAgICAgICAgY2FzZSAnb2JqZWN0JzoKICAgICAgICAgICAgaWYgKHZhbHVlIGluc3RhbmNlb2YgRGF0ZSkgewogICAgICAgICAgICAgIHJldHVybiB2YWx1ZS50b1N0cmluZygpOwogICAgICAgICAgICB9IGVsc2UgaWYgKHZhbHVlKSB7CiAgICAgICAgICAgICAgdHJ5IHsKICAgICAgICAgICAgICAgIHJldHVybiBKU09OLnN0cmluZ2lmeSh2YWx1ZSk7CiAgICAgICAgICAgICAgfSBjYXRjaCh4KSB7CiAgICAgICAgICAgICAgICByZXR1cm4gJyc7CiAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CgogICAgICAgICAgZGVmYXVsdDoKICAgICAgICAgICAgcmV0dXJuIHN1cGVyLl9zZXJpYWxpemVWYWx1ZSh2YWx1ZSk7CiAgICAgICAgfQogICAgICB9CgogICAgICAvKioKICAgICAgICogQ29udmVydHMgYSBzdHJpbmcgdG8gYSB0eXBlZCBKYXZhU2NyaXB0IHZhbHVlLgogICAgICAgKgogICAgICAgKiBUaGlzIG1ldGhvZCBpcyBjYWxsZWQgYnkgUG9seW1lciB3aGVuIHJlYWRpbmcgSFRNTCBhdHRyaWJ1dGUgdmFsdWVzIHRvCiAgICAgICAqIEpTIHByb3BlcnRpZXMuICBVc2VycyBtYXkgb3ZlcnJpZGUgdGhpcyBtZXRob2Qgb24gUG9seW1lciBlbGVtZW50CiAgICAgICAqIHByb3RvdHlwZXMgdG8gcHJvdmlkZSBkZXNlcmlhbGl6YXRpb24gZm9yIGN1c3RvbSBgdHlwZWBzLiAgTm90ZSwKICAgICAgICogdGhlIGB0eXBlYCBhcmd1bWVudCBpcyB0aGUgdmFsdWUgb2YgdGhlIGB0eXBlYCBmaWVsZCBwcm92aWRlZCBpbiB0aGUKICAgICAgICogYHByb3BlcnRpZXNgIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGZvciBhIGdpdmVuIHByb3BlcnR5LCBhbmQgaXMKICAgICAgICogYnkgY29udmVudGlvbiB0aGUgY29uc3RydWN0b3IgZm9yIHRoZSB0eXBlIHRvIGRlc2VyaWFsaXplLgogICAgICAgKgogICAgICAgKgogICAgICAgKiBAcGFyYW0gez9zdHJpbmd9IHZhbHVlIEF0dHJpYnV0ZSB2YWx1ZSB0byBkZXNlcmlhbGl6ZS4KICAgICAgICogQHBhcmFtIHsqPX0gdHlwZSBUeXBlIHRvIGRlc2VyaWFsaXplIHRoZSBzdHJpbmcgdG8uCiAgICAgICAqIEByZXR1cm4geyp9IFR5cGVkIHZhbHVlIGRlc2VyaWFsaXplZCBmcm9tIHRoZSBwcm92aWRlZCBzdHJpbmcuCiAgICAgICAqLwogICAgICBfZGVzZXJpYWxpemVWYWx1ZSh2YWx1ZSwgdHlwZSkgewogICAgICAgIC8qKgogICAgICAgICAqIEB0eXBlIHsqfQogICAgICAgICAqLwogICAgICAgIGxldCBvdXRWYWx1ZTsKICAgICAgICBzd2l0Y2ggKHR5cGUpIHsKICAgICAgICAgIGNhc2UgT2JqZWN0OgogICAgICAgICAgICB0cnkgewogICAgICAgICAgICAgIG91dFZhbHVlID0gSlNPTi5wYXJzZSgvKiogQHR5cGUge3N0cmluZ30gKi8odmFsdWUpKTsKICAgICAgICAgICAgfSBjYXRjaCh4KSB7CiAgICAgICAgICAgICAgLy8gYWxsb3cgbm9uLUpTT04gbGl0ZXJhbHMgbGlrZSBTdHJpbmdzIGFuZCBOdW1iZXJzCiAgICAgICAgICAgICAgb3V0VmFsdWUgPSB2YWx1ZTsKICAgICAgICAgICAgfQogICAgICAgICAgICBicmVhazsKICAgICAgICAgIGNhc2UgQXJyYXk6CiAgICAgICAgICAgIHRyeSB7CiAgICAgICAgICAgICAgb3V0VmFsdWUgPSBKU09OLnBhcnNlKC8qKiBAdHlwZSB7c3RyaW5nfSAqLyh2YWx1ZSkpOwogICAgICAgICAgICB9IGNhdGNoKHgpIHsKICAgICAgICAgICAgICBvdXRWYWx1ZSA9IG51bGw7CiAgICAgICAgICAgICAgY29uc29sZS53YXJuKGBQb2x5bWVyOjpBdHRyaWJ1dGVzOiBjb3VsZG4ndCBkZWNvZGUgQXJyYXkgYXMgSlNPTjogJHt2YWx1ZX1gKTsKICAgICAgICAgICAgfQogICAgICAgICAgICBicmVhazsKICAgICAgICAgIGNhc2UgRGF0ZToKICAgICAgICAgICAgb3V0VmFsdWUgPSBpc05hTih2YWx1ZSkgPyBTdHJpbmcodmFsdWUpIDogTnVtYmVyKHZhbHVlKTsKICAgICAgICAgICAgb3V0VmFsdWUgPSBuZXcgRGF0ZShvdXRWYWx1ZSk7CiAgICAgICAgICAgIGJyZWFrOwogICAgICAgICAgZGVmYXVsdDoKICAgICAgICAgICAgb3V0VmFsdWUgPSBzdXBlci5fZGVzZXJpYWxpemVWYWx1ZSh2YWx1ZSwgdHlwZSk7CiAgICAgICAgICAgIGJyZWFrOwogICAgICAgIH0KICAgICAgICByZXR1cm4gb3V0VmFsdWU7CiAgICAgIH0KICAgICAgLyogZXNsaW50LWVuYWJsZSBuby1mYWxsdGhyb3VnaCAqLwoKICAgICAgLyoqCiAgICAgICAqIE92ZXJyaWRlcyBQcm9wZXJ0aWVzQ2hhbmdlZCBpbXBsZW1lbnRhdGlvbiB0byBzYXZlIGV4aXN0aW5nIHByb3RvdHlwZQogICAgICAgKiBwcm9wZXJ0eSB2YWx1ZSBzbyB0aGF0IGl0IGNhbiBiZSByZXNldC4KICAgICAgICogQHBhcmFtIHtzdHJpbmd9IHByb3BlcnR5IE5hbWUgb2YgdGhlIHByb3BlcnR5CiAgICAgICAqIEBwYXJhbSB7Ym9vbGVhbj19IHJlYWRPbmx5IFdoZW4gdHJ1ZSwgbm8gc2V0dGVyIGlzIGNyZWF0ZWQKICAgICAgICoKICAgICAgICogV2hlbiBjYWxsaW5nIG9uIGEgcHJvdG90eXBlLCBhbnkgb3ZlcndyaXR0ZW4gdmFsdWVzIGFyZSBzYXZlZCBpbgogICAgICAgKiBgX19kYXRhUHJvdG9gLCBhbmQgaXQgaXMgdXAgdG8gdGhlIHN1YmNsYXNzZXIgdG8gZGVjaWRlIGhvdy93aGVuCiAgICAgICAqIHRvIHNldCB0aG9zZSBwcm9wZXJ0aWVzIGJhY2sgaW50byB0aGUgYWNjZXNzb3IuICBXaGVuIGNhbGxpbmcgb24gYW4KICAgICAgICogaW5zdGFuY2UsIHRoZSBvdmVyd3JpdHRlbiB2YWx1ZSBpcyBzZXQgdmlhIGBfc2V0UGVuZGluZ1Byb3BlcnR5YCwKICAgICAgICogYW5kIHRoZSB1c2VyIHNob3VsZCBjYWxsIGBfaW52YWxpZGF0ZVByb3BlcnRpZXNgIG9yIGBfZmx1c2hQcm9wZXJ0aWVzYAogICAgICAgKiBmb3IgdGhlIHZhbHVlcyB0byB0YWtlIGVmZmVjdC4KICAgICAgICogQHByb3RlY3RlZAogICAgICAgKiBAcmV0dXJuIHt2b2lkfQogICAgICAgKi8KICAgICAgX2RlZmluZVByb3BlcnR5QWNjZXNzb3IocHJvcGVydHksIHJlYWRPbmx5KSB7CiAgICAgICAgc2F2ZUFjY2Vzc29yVmFsdWUodGhpcywgcHJvcGVydHkpOwogICAgICAgIHN1cGVyLl9kZWZpbmVQcm9wZXJ0eUFjY2Vzc29yKHByb3BlcnR5LCByZWFkT25seSk7CiAgICAgIH0KCiAgICAgIC8qKgogICAgICAgKiBSZXR1cm5zIHRydWUgaWYgdGhpcyBsaWJyYXJ5IGNyZWF0ZWQgYW4gYWNjZXNzb3IgZm9yIHRoZSBnaXZlbiBwcm9wZXJ0eS4KICAgICAgICoKICAgICAgICogQHBhcmFtIHtzdHJpbmd9IHByb3BlcnR5IFByb3BlcnR5IG5hbWUKICAgICAgICogQHJldHVybiB7Ym9vbGVhbn0gVHJ1ZSBpZiBhbiBhY2Nlc3NvciB3YXMgY3JlYXRlZAogICAgICAgKi8KICAgICAgX2hhc0FjY2Vzc29yKHByb3BlcnR5KSB7CiAgICAgICAgcmV0dXJuIHRoaXMuX19kYXRhSGFzQWNjZXNzb3IgJiYgdGhpcy5fX2RhdGFIYXNBY2Nlc3Nvcltwcm9wZXJ0eV07CiAgICAgIH0KCiAgICAgIC8qKgogICAgICAgKiBSZXR1cm5zIHRydWUgaWYgdGhlIHNwZWNpZmllZCBwcm9wZXJ0eSBoYXMgYSBwZW5kaW5nIGNoYW5nZS4KICAgICAgICoKICAgICAgICogQHBhcmFtIHtzdHJpbmd9IHByb3AgUHJvcGVydHkgbmFtZQogICAgICAgKiBAcmV0dXJuIHtib29sZWFufSBUcnVlIGlmIHByb3BlcnR5IGhhcyBhIHBlbmRpbmcgY2hhbmdlCiAgICAgICAqIEBwcm90ZWN0ZWQKICAgICAgICovCiAgICAgIF9pc1Byb3BlcnR5UGVuZGluZyhwcm9wKSB7CiAgICAgICAgcmV0dXJuIEJvb2xlYW4odGhpcy5fX2RhdGFQZW5kaW5nICYmIChwcm9wIGluIHRoaXMuX19kYXRhUGVuZGluZykpOwogICAgICB9CgogICAgfQoKICAgIHJldHVybiBQcm9wZXJ0eUFjY2Vzc29yczsKCiAgfSk7Cgp9KSgpOwo8L3NjcmlwdD48c2NyaXB0PgooZnVuY3Rpb24oKSB7CgogICd1c2Ugc3RyaWN0JzsKCiAgY29uc3Qgd2Fsa2VyID0gZG9jdW1lbnQuY3JlYXRlVHJlZVdhbGtlcihkb2N1bWVudCwgTm9kZUZpbHRlci5TSE9XX0FMTCwKICAgICAgbnVsbCwgZmFsc2UpOwoKICAvLyAxLnggYmFja3dhcmRzLWNvbXBhdGlibGUgYXV0by13cmFwcGVyIGZvciB0ZW1wbGF0ZSB0eXBlIGV4dGVuc2lvbnMKICAvLyBUaGlzIGlzIGEgY2xlYXIgbGF5ZXJpbmcgdmlvbGF0aW9uIGFuZCBnaXZlcyBmYXZvcmVkLW5hdGlvbiBzdGF0dXMgdG8KICAvLyBkb20taWYgYW5kIGRvbS1yZXBlYXQgdGVtcGxhdGVzLiAgVGhpcyBpcyBhIGNvbmNlaXQgd2UncmUgY2hvb3NpbmcgdG8ga2VlcAogIC8vIGEuKSB0byBlYXNlIDEueCBiYWNrd2FyZHMtY29tcGF0aWJpbGl0eSBkdWUgdG8gbG9zcyBvZiBgaXNgLCBhbmQKICAvLyBiLikgdG8gbWFpbnRhaW4gaWYvcmVwZWF0IGNhcGFiaWxpdHkgaW4gcGFyc2VyLWNvbnN0cmFpbmVkIGVsZW1lbnRzCiAgLy8gICAgIChlLmcuIHRhYmxlLCBzZWxlY3QpIGluIGxpZXUgb2YgbmF0aXZlIENFIHR5cGUgZXh0ZW5zaW9ucyB3aXRob3V0CiAgLy8gICAgIG1hc3NpdmUgbmV3IGludmVudGlvbiBpbiB0aGlzIHNwYWNlIChlLmcuIGRpcmVjdGl2ZSBzeXN0ZW0pCiAgY29uc3QgdGVtcGxhdGVFeHRlbnNpb25zID0gewogICAgJ2RvbS1pZic6IHRydWUsCiAgICAnZG9tLXJlcGVhdCc6IHRydWUKICB9OwogIGZ1bmN0aW9uIHdyYXBUZW1wbGF0ZUV4dGVuc2lvbihub2RlKSB7CiAgICBsZXQgaXMgPSBub2RlLmdldEF0dHJpYnV0ZSgnaXMnKTsKICAgIGlmIChpcyAmJiB0ZW1wbGF0ZUV4dGVuc2lvbnNbaXNdKSB7CiAgICAgIGxldCB0ID0gbm9kZTsKICAgICAgdC5yZW1vdmVBdHRyaWJ1dGUoJ2lzJyk7CiAgICAgIG5vZGUgPSB0Lm93bmVyRG9jdW1lbnQuY3JlYXRlRWxlbWVudChpcyk7CiAgICAgIHQucGFyZW50Tm9kZS5yZXBsYWNlQ2hpbGQobm9kZSwgdCk7CiAgICAgIG5vZGUuYXBwZW5kQ2hpbGQodCk7CiAgICAgIHdoaWxlKHQuYXR0cmlidXRlcy5sZW5ndGgpIHsKICAgICAgICBub2RlLnNldEF0dHJpYnV0ZSh0LmF0dHJpYnV0ZXNbMF0ubmFtZSwgdC5hdHRyaWJ1dGVzWzBdLnZhbHVlKTsKICAgICAgICB0LnJlbW92ZUF0dHJpYnV0ZSh0LmF0dHJpYnV0ZXNbMF0ubmFtZSk7CiAgICAgIH0KICAgIH0KICAgIHJldHVybiBub2RlOwogIH0KCiAgZnVuY3Rpb24gZmluZFRlbXBsYXRlTm9kZShyb290LCBub2RlSW5mbykgewogICAgLy8gcmVjdXJzaXZlbHkgYXNjZW5kIHRyZWUgdW50aWwgd2UgaGl0IHJvb3QKICAgIGxldCBwYXJlbnQgPSBub2RlSW5mby5wYXJlbnRJbmZvICYmIGZpbmRUZW1wbGF0ZU5vZGUocm9vdCwgbm9kZUluZm8ucGFyZW50SW5mbyk7CiAgICAvLyB1bndpbmQgdGhlIHN0YWNrLCByZXR1cm5pbmcgdGhlIGluZGV4ZWQgbm9kZSBhdCBlYWNoIGxldmVsCiAgICBpZiAocGFyZW50KSB7CiAgICAgIC8vIG5vdGU6IG1hcmdpbmFsbHkgZmFzdGVyIHRoYW4gaW5kZXhpbmcgdmlhIGNoaWxkTm9kZXMKICAgICAgLy8gKGh0dHA6Ly9qc3BlcmYuY29tL2NoaWxkbm9kZXMtbG9va3VwKQogICAgICB3YWxrZXIuY3VycmVudE5vZGUgPSBwYXJlbnQ7CiAgICAgIGZvciAobGV0IG49d2Fsa2VyLmZpcnN0Q2hpbGQoKSwgaT0wOyBuOyBuPXdhbGtlci5uZXh0U2libGluZygpKSB7CiAgICAgICAgaWYgKG5vZGVJbmZvLnBhcmVudEluZGV4ID09PSBpKyspIHsKICAgICAgICAgIHJldHVybiBuOwogICAgICAgIH0KICAgICAgfQogICAgfSBlbHNlIHsKICAgICAgcmV0dXJuIHJvb3Q7CiAgICB9CiAgfQoKICAvLyBjb25zdHJ1Y3QgYCRgIG1hcCAoZnJvbSBpZCBhbm5vdGF0aW9ucykKICBmdW5jdGlvbiBhcHBseUlkVG9NYXAoaW5zdCwgbWFwLCBub2RlLCBub2RlSW5mbykgewogICAgaWYgKG5vZGVJbmZvLmlkKSB7CiAgICAgIG1hcFtub2RlSW5mby5pZF0gPSBub2RlOwogICAgfQogIH0KCiAgLy8gaW5zdGFsbCBldmVudCBsaXN0ZW5lcnMgKGZyb20gZXZlbnQgYW5ub3RhdGlvbnMpCiAgZnVuY3Rpb24gYXBwbHlFdmVudExpc3RlbmVyKGluc3QsIG5vZGUsIG5vZGVJbmZvKSB7CiAgICBpZiAobm9kZUluZm8uZXZlbnRzICYmIG5vZGVJbmZvLmV2ZW50cy5sZW5ndGgpIHsKICAgICAgZm9yIChsZXQgaj0wLCBlJD1ub2RlSW5mby5ldmVudHMsIGU7IChqPGUkLmxlbmd0aCkgJiYgKGU9ZSRbal0pOyBqKyspIHsKICAgICAgICBpbnN0Ll9hZGRNZXRob2RFdmVudExpc3RlbmVyVG9Ob2RlKG5vZGUsIGUubmFtZSwgZS52YWx1ZSwgaW5zdCk7CiAgICAgIH0KICAgIH0KICB9CgogIC8vIHB1c2ggY29uZmlndXJhdGlvbiByZWZlcmVuY2VzIGF0IGNvbmZpZ3VyZSB0aW1lCiAgZnVuY3Rpb24gYXBwbHlUZW1wbGF0ZUNvbnRlbnQoaW5zdCwgbm9kZSwgbm9kZUluZm8pIHsKICAgIGlmIChub2RlSW5mby50ZW1wbGF0ZUluZm8pIHsKICAgICAgbm9kZS5fdGVtcGxhdGVJbmZvID0gbm9kZUluZm8udGVtcGxhdGVJbmZvOwogICAgfQogIH0KCiAgZnVuY3Rpb24gY3JlYXRlTm9kZUV2ZW50SGFuZGxlcihjb250ZXh0LCBldmVudE5hbWUsIG1ldGhvZE5hbWUpIHsKICAgIC8vIEluc3RhbmNlcyBjYW4gb3B0aW9uYWxseSBoYXZlIGEgX21ldGhvZEhvc3Qgd2hpY2ggYWxsb3dzIHJlZGlyZWN0aW5nIHdoZXJlCiAgICAvLyB0byBmaW5kIG1ldGhvZHMuIEN1cnJlbnRseSB1c2VkIGJ5IGB0ZW1wbGF0aXplYC4KICAgIGNvbnRleHQgPSBjb250ZXh0Ll9tZXRob2RIb3N0IHx8IGNvbnRleHQ7CiAgICBsZXQgaGFuZGxlciA9IGZ1bmN0aW9uKGUpIHsKICAgICAgaWYgKGNvbnRleHRbbWV0aG9kTmFtZV0pIHsKICAgICAgICBjb250ZXh0W21ldGhvZE5hbWVdKGUsIGUuZGV0YWlsKTsKICAgICAgfSBlbHNlIHsKICAgICAgICBjb25zb2xlLndhcm4oJ2xpc3RlbmVyIG1ldGhvZCBgJyArIG1ldGhvZE5hbWUgKyAnYCBub3QgZGVmaW5lZCcpOwogICAgICB9CiAgICB9OwogICAgcmV0dXJuIGhhbmRsZXI7CiAgfQoKICAvKioKICAgKiBFbGVtZW50IG1peGluIHRoYXQgcHJvdmlkZXMgYmFzaWMgdGVtcGxhdGUgcGFyc2luZyBhbmQgc3RhbXBpbmcsIGluY2x1ZGluZwogICAqIHRoZSBmb2xsb3dpbmcgdGVtcGxhdGUtcmVsYXRlZCBmZWF0dXJlcyBmb3Igc3RhbXBlZCB0ZW1wbGF0ZXM6CiAgICoKICAgKiAtIERlY2xhcmF0aXZlIGV2ZW50IGxpc3RlbmVycyAoYG9uLWV2ZW50bmFtZT0ibGlzdGVuZXIiYCkKICAgKiAtIE1hcCBvZiBub2RlIGlkJ3MgdG8gc3RhbXBlZCBub2RlIGluc3RhbmNlcyAoYHRoaXMuJC5pZGApCiAgICogLSBOZXN0ZWQgdGVtcGxhdGUgY29udGVudCBjYWNoaW5nL3JlbW92YWwgYW5kIHJlLWluc3RhbGxhdGlvbiAocGVyZm9ybWFuY2UKICAgKiAgIG9wdGltaXphdGlvbikKICAgKgogICAqIEBtaXhpbkZ1bmN0aW9uCiAgICogQHBvbHltZXIKICAgKiBAbWVtYmVyb2YgUG9seW1lcgogICAqIEBzdW1tYXJ5IEVsZW1lbnQgY2xhc3MgbWl4aW4gdGhhdCBwcm92aWRlcyBiYXNpYyB0ZW1wbGF0ZSBwYXJzaW5nIGFuZCBzdGFtcGluZwogICAqLwogIFBvbHltZXIuVGVtcGxhdGVTdGFtcCA9IFBvbHltZXIuZGVkdXBpbmdNaXhpbihzdXBlckNsYXNzID0+IHsKCiAgICAvKioKICAgICAqIEBwb2x5bWVyCiAgICAgKiBAbWl4aW5DbGFzcwogICAgICogQGltcGxlbWVudHMge1BvbHltZXJfVGVtcGxhdGVTdGFtcH0KICAgICAqLwogICAgY2xhc3MgVGVtcGxhdGVTdGFtcCBleHRlbmRzIHN1cGVyQ2xhc3MgewoKICAgICAgLyoqCiAgICAgICAqIFNjYW5zIGEgdGVtcGxhdGUgdG8gcHJvZHVjZSB0ZW1wbGF0ZSBtZXRhZGF0YS4KICAgICAgICoKICAgICAgICogVGVtcGxhdGUtc3BlY2lmaWMgbWV0YWRhdGEgYXJlIHN0b3JlZCBpbiB0aGUgb2JqZWN0IHJldHVybmVkLCBhbmQgbm9kZS0KICAgICAgICogc3BlY2lmaWMgbWV0YWRhdGEgYXJlIHN0b3JlZCBpbiBvYmplY3RzIGluIGl0cyBmbGF0dGVuZWQgYG5vZGVJbmZvTGlzdGAKICAgICAgICogYXJyYXkuICBPbmx5IG5vZGVzIGluIHRoZSB0ZW1wbGF0ZSB0aGF0IHdlcmUgcGFyc2VkIGFzIG5vZGVzIG9mCiAgICAgICAqIGludGVyZXN0IGNvbnRhaW4gYW4gb2JqZWN0IGluIGBub2RlSW5mb0xpc3RgLiAgRWFjaCBgbm9kZUluZm9gIG9iamVjdAogICAgICAgKiBjb250YWlucyBhbiBgaW5kZXhgIChgY2hpbGROb2Rlc2AgaW5kZXggaW4gcGFyZW50KSBhbmQgb3B0aW9uYWxseQogICAgICAgKiBgcGFyZW50YCwgd2hpY2ggcG9pbnRzIHRvIG5vZGUgaW5mbyBvZiBpdHMgcGFyZW50IChpbmNsdWRpbmcgaXRzIGluZGV4KS4KICAgICAgICoKICAgICAgICogVGhlIHRlbXBsYXRlIG1ldGFkYXRhIG9iamVjdCByZXR1cm5lZCBmcm9tIHRoaXMgbWV0aG9kIGhhcyB0aGUgZm9sbG93aW5nCiAgICAgICAqIHN0cnVjdHVyZSAobWFueSBmaWVsZHMgb3B0aW9uYWwpOgogICAgICAgKgogICAgICAgKiBgYGBqcwogICAgICAgKiAgIHsKICAgICAgICogICAgIC8vIEZsYXR0ZW5lZCBsaXN0IG9mIG5vZGUgbWV0YWRhdGEgKGZvciBub2RlcyB0aGF0IGdlbmVyYXRlZCBtZXRhZGF0YSkKICAgICAgICogICAgIG5vZGVJbmZvTGlzdDogWwogICAgICAgKiAgICAgICB7CiAgICAgICAqICAgICAgICAgLy8gYGlkYCBhdHRyaWJ1dGUgZm9yIGFueSBub2RlcyB3aXRoIGlkJ3MgZm9yIGdlbmVyYXRpbmcgYCRgIG1hcAogICAgICAgKiAgICAgICAgIGlkOiB7c3RyaW5nfSwKICAgICAgICogICAgICAgICAvLyBgb24tZXZlbnQ9ImhhbmRsZXIiYCBtZXRhZGF0YQogICAgICAgKiAgICAgICAgIGV2ZW50czogWwogICAgICAgKiAgICAgICAgICAgewogICAgICAgKiAgICAgICAgICAgICBuYW1lOiB7c3RyaW5nfSwgICAvLyBldmVudCBuYW1lCiAgICAgICAqICAgICAgICAgICAgIHZhbHVlOiB7c3RyaW5nfSwgIC8vIGhhbmRsZXIgbWV0aG9kIG5hbWUKICAgICAgICogICAgICAgICAgIH0sIC4uLgogICAgICAgKiAgICAgICAgIF0sCiAgICAgICAqICAgICAgICAgLy8gTm90ZXMgd2hlbiB0aGUgdGVtcGxhdGUgY29udGFpbmVkIGEgYDxzbG90PmAgZm9yIHNoYWR5IERPTQogICAgICAgKiAgICAgICAgIC8vIG9wdGltaXphdGlvbiBwdXJwb3NlcwogICAgICAgKiAgICAgICAgIGhhc0luc2VydGlvblBvaW50OiB7Ym9vbGVhbn0sCiAgICAgICAqICAgICAgICAgLy8gRm9yIG5lc3RlZCBgPHRlbXBsYXRlPmBgIG5vZGVzLCBuZXN0ZWQgdGVtcGxhdGUgbWV0YWRhdGEKICAgICAgICogICAgICAgICB0ZW1wbGF0ZUluZm86IHtvYmplY3R9LCAvLyBuZXN0ZWQgdGVtcGxhdGUgbWV0YWRhdGEKICAgICAgICogICAgICAgICAvLyBNZXRhZGF0YSB0byBhbGxvdyBlZmZpY2llbnQgcmV0cmlldmFsIG9mIGluc3RhbmNlZCBub2RlCiAgICAgICAqICAgICAgICAgLy8gY29ycmVzcG9uZGluZyB0byB0aGlzIG1ldGFkYXRhCiAgICAgICAqICAgICAgICAgcGFyZW50SW5mbzoge251bWJlcn0sICAgLy8gcmVmZXJlbmNlIHRvIHBhcmVudCBub2RlSW5mbz4KICAgICAgICogICAgICAgICBwYXJlbnRJbmRleDoge251bWJlcn0sICAvLyBpbmRleCBpbiBwYXJlbnQncyBgY2hpbGROb2Rlc2AgY29sbGVjdGlvbgogICAgICAgKiAgICAgICAgIGluZm9JbmRleDoge251bWJlcn0sICAgIC8vIGluZGV4IG9mIHRoaXMgYG5vZGVJbmZvYCBpbiBgdGVtcGxhdGVJbmZvLm5vZGVJbmZvTGlzdGAKICAgICAgICogICAgICAgfSwKICAgICAgICogICAgICAgLi4uCiAgICAgICAqICAgICBdLAogICAgICAgKiAgICAgLy8gV2hlbiB0cnVlLCB0aGUgdGVtcGxhdGUgaGFkIHRoZSBgc3RyaXAtd2hpdGVzcGFjZWAgYXR0cmlidXRlCiAgICAgICAqICAgICAvLyBvciB3YXMgbmVzdGVkIGluIGEgdGVtcGxhdGUgd2l0aCB0aGF0IHNldHRpbmcKICAgICAgICogICAgIHN0cmlwV2hpdGVzcGFjZToge2Jvb2xlYW59LAogICAgICAgKiAgICAgLy8gRm9yIG5lc3RlZCB0ZW1wbGF0ZXMsIG5lc3RlZCB0ZW1wbGF0ZSBjb250ZW50IGlzIG1vdmVkIGludG8KICAgICAgICogICAgIC8vIGEgZG9jdW1lbnQgZnJhZ21lbnQgc3RvcmVkIGhlcmU7IHRoaXMgaXMgYW4gb3B0aW1pemF0aW9uIHRvCiAgICAgICAqICAgICAvLyBhdm9pZCB0aGUgY29zdCBvZiBuZXN0ZWQgdGVtcGxhdGUgY2xvbmluZwogICAgICAgKiAgICAgY29udGVudDoge0RvY3VtZW50RnJhZ21lbnR9CiAgICAgICAqICAgfQogICAgICAgKiBgYGAKICAgICAgICoKICAgICAgICogVGhpcyBtZXRob2Qga2lja3Mgb2ZmIGEgcmVjdXJzaXZlIHRyZWV3YWxrIGFzIGZvbGxvd3M6CiAgICAgICAqCiAgICAgICAqIGBgYAogICAgICAgKiAgICBfcGFyc2VUZW1wbGF0ZSA8LS0tLS0tLS0tLS0tLS0tLS0tLS0tKwogICAgICAgKiAgICAgIF9wYXJzZVRlbXBsYXRlQ29udGVudCAgICAgICAgICAgICAgfAogICAgICAgKiAgICAgICAgX3BhcnNlVGVtcGxhdGVOb2RlICA8LS0tLS0tLS0tLS0tfC0tKwogICAgICAgKiAgICAgICAgICBfcGFyc2VUZW1wbGF0ZU5lc3RlZFRlbXBsYXRlIC0tKyAgfAogICAgICAgKiAgICAgICAgICBfcGFyc2VUZW1wbGF0ZUNoaWxkTm9kZXMgLS0tLS0tLS0tKwogICAgICAgKiAgICAgICAgICBfcGFyc2VUZW1wbGF0ZU5vZGVBdHRyaWJ1dGVzCiAgICAgICAqICAgICAgICAgICAgX3BhcnNlVGVtcGxhdGVOb2RlQXR0cmlidXRlCiAgICAgICAqCiAgICAgICAqIGBgYAogICAgICAgKgogICAgICAgKiBUaGVzZSBtZXRob2RzIG1heSBiZSBvdmVycmlkZGVuIHRvIGFkZCBjdXN0b20gbWV0YWRhdGEgYWJvdXQgdGVtcGxhdGVzCiAgICAgICAqIHRvIGVpdGhlciBgdGVtcGxhdGVJbmZvYCBvciBgbm9kZUluZm9gLgogICAgICAgKgogICAgICAgKiBOb3RlIHRoYXQgdGhpcyBtZXRob2QgbWF5IGJlIGRlc3RydWN0aXZlIHRvIHRoZSB0ZW1wbGF0ZSwgaW4gdGhhdAogICAgICAgKiBlLmcuIGV2ZW50IGFubm90YXRpb25zIG1heSBiZSByZW1vdmVkIGFmdGVyIGJlaW5nIG5vdGVkIGluIHRoZQogICAgICAgKiB0ZW1wbGF0ZSBtZXRhZGF0YS4KICAgICAgICoKICAgICAgICogQHBhcmFtIHshSFRNTFRlbXBsYXRlRWxlbWVudH0gdGVtcGxhdGUgVGVtcGxhdGUgdG8gcGFyc2UKICAgICAgICogQHBhcmFtIHtUZW1wbGF0ZUluZm89fSBvdXRlclRlbXBsYXRlSW5mbyBUZW1wbGF0ZSBtZXRhZGF0YSBmcm9tIHRoZSBvdXRlcgogICAgICAgKiAgIHRlbXBsYXRlLCBmb3IgcGFyc2luZyBuZXN0ZWQgdGVtcGxhdGVzCiAgICAgICAqIEByZXR1cm4geyFUZW1wbGF0ZUluZm99IFBhcnNlZCB0ZW1wbGF0ZSBtZXRhZGF0YQogICAgICAgKi8KICAgICAgc3RhdGljIF9wYXJzZVRlbXBsYXRlKHRlbXBsYXRlLCBvdXRlclRlbXBsYXRlSW5mbykgewogICAgICAgIC8vIHNpbmNlIGEgdGVtcGxhdGUgbWF5IGJlIHJlLXVzZWQsIG1lbW8taXplIG1ldGFkYXRhCiAgICAgICAgaWYgKCF0ZW1wbGF0ZS5fdGVtcGxhdGVJbmZvKSB7CiAgICAgICAgICBsZXQgdGVtcGxhdGVJbmZvID0gdGVtcGxhdGUuX3RlbXBsYXRlSW5mbyA9IHt9OwogICAgICAgICAgdGVtcGxhdGVJbmZvLm5vZGVJbmZvTGlzdCA9IFtdOwogICAgICAgICAgdGVtcGxhdGVJbmZvLnN0cmlwV2hpdGVTcGFjZSA9IFBvbHltZXIubGVnYWN5T3B0aW1pemF0aW9ucyB8fAogICAgICAgICAgICAob3V0ZXJUZW1wbGF0ZUluZm8gJiYgb3V0ZXJUZW1wbGF0ZUluZm8uc3RyaXBXaGl0ZVNwYWNlKSB8fAogICAgICAgICAgICB0ZW1wbGF0ZS5oYXNBdHRyaWJ1dGUoJ3N0cmlwLXdoaXRlc3BhY2UnKTsKICAgICAgICAgIHRoaXMuX3BhcnNlVGVtcGxhdGVDb250ZW50KHRlbXBsYXRlLCB0ZW1wbGF0ZUluZm8sIHtwYXJlbnQ6IG51bGx9KTsKICAgICAgICB9CiAgICAgICAgcmV0dXJuIHRlbXBsYXRlLl90ZW1wbGF0ZUluZm87CiAgICAgIH0KCiAgICAgIHN0YXRpYyBfcGFyc2VUZW1wbGF0ZUNvbnRlbnQodGVtcGxhdGUsIHRlbXBsYXRlSW5mbywgbm9kZUluZm8pIHsKICAgICAgICByZXR1cm4gdGhpcy5fcGFyc2VUZW1wbGF0ZU5vZGUodGVtcGxhdGUuY29udGVudCwgdGVtcGxhdGVJbmZvLCBub2RlSW5mbyk7CiAgICAgIH0KCiAgICAgIC8qKgogICAgICAgKiBQYXJzZXMgdGVtcGxhdGUgbm9kZSBhbmQgYWRkcyB0ZW1wbGF0ZSBhbmQgbm9kZSBtZXRhZGF0YSBiYXNlZCBvbgogICAgICAgKiB0aGUgY3VycmVudCBub2RlLCBhbmQgaXRzIGBjaGlsZE5vZGVzYCBhbmQgYGF0dHJpYnV0ZXNgLgogICAgICAgKgogICAgICAgKiBUaGlzIG1ldGhvZCBtYXkgYmUgb3ZlcnJpZGRlbiB0byBhZGQgY3VzdG9tIG5vZGUgb3IgdGVtcGxhdGUgc3BlY2lmaWMKICAgICAgICogbWV0YWRhdGEgYmFzZWQgb24gdGhpcyBub2RlLgogICAgICAgKgogICAgICAgKiBAcGFyYW0ge05vZGV9IG5vZGUgTm9kZSB0byBwYXJzZQogICAgICAgKiBAcGFyYW0geyFUZW1wbGF0ZUluZm99IHRlbXBsYXRlSW5mbyBUZW1wbGF0ZSBtZXRhZGF0YSBmb3IgY3VycmVudCB0ZW1wbGF0ZQogICAgICAgKiBAcGFyYW0geyFOb2RlSW5mb30gbm9kZUluZm8gTm9kZSBtZXRhZGF0YSBmb3IgY3VycmVudCB0ZW1wbGF0ZS4KICAgICAgICogQHJldHVybiB7Ym9vbGVhbn0gYHRydWVgIGlmIHRoZSB2aXNpdGVkIG5vZGUgYWRkZWQgbm9kZS1zcGVjaWZpYwogICAgICAgKiAgIG1ldGFkYXRhIHRvIGBub2RlSW5mb2AKICAgICAgICovCiAgICAgIHN0YXRpYyBfcGFyc2VUZW1wbGF0ZU5vZGUobm9kZSwgdGVtcGxhdGVJbmZvLCBub2RlSW5mbykgewogICAgICAgIGxldCBub3RlZDsKICAgICAgICBsZXQgZWxlbWVudCA9IC8qKiBAdHlwZSB7RWxlbWVudH0gKi8obm9kZSk7CiAgICAgICAgaWYgKGVsZW1lbnQubG9jYWxOYW1lID09ICd0ZW1wbGF0ZScgJiYgIWVsZW1lbnQuaGFzQXR0cmlidXRlKCdwcmVzZXJ2ZS1jb250ZW50JykpIHsKICAgICAgICAgIG5vdGVkID0gdGhpcy5fcGFyc2VUZW1wbGF0ZU5lc3RlZFRlbXBsYXRlKGVsZW1lbnQsIHRlbXBsYXRlSW5mbywgbm9kZUluZm8pIHx8IG5vdGVkOwogICAgICAgIH0gZWxzZSBpZiAoZWxlbWVudC5sb2NhbE5hbWUgPT09ICdzbG90JykgewogICAgICAgICAgLy8gRm9yIFNoYWR5RG9tIG9wdGltaXphdGlvbiwgaW5kaWNhdGluZyB0aGVyZSBpcyBhbiBpbnNlcnRpb24gcG9pbnQKICAgICAgICAgIHRlbXBsYXRlSW5mby5oYXNJbnNlcnRpb25Qb2ludCA9IHRydWU7CiAgICAgICAgfQogICAgICAgIHdhbGtlci5jdXJyZW50Tm9kZSA9IGVsZW1lbnQ7CiAgICAgICAgaWYgKHdhbGtlci5maXJzdENoaWxkKCkpIHsKICAgICAgICAgIG5vdGVkID0gdGhpcy5fcGFyc2VUZW1wbGF0ZUNoaWxkTm9kZXMoZWxlbWVudCwgdGVtcGxhdGVJbmZvLCBub2RlSW5mbykgfHwgbm90ZWQ7CiAgICAgICAgfQogICAgICAgIGlmIChlbGVtZW50Lmhhc0F0dHJpYnV0ZXMgJiYgZWxlbWVudC5oYXNBdHRyaWJ1dGVzKCkpIHsKICAgICAgICAgIG5vdGVkID0gdGhpcy5fcGFyc2VUZW1wbGF0ZU5vZGVBdHRyaWJ1dGVzKGVsZW1lbnQsIHRlbXBsYXRlSW5mbywgbm9kZUluZm8pIHx8IG5vdGVkOwogICAgICAgIH0KICAgICAgICByZXR1cm4gbm90ZWQ7CiAgICAgIH0KCiAgICAgIC8qKgogICAgICAgKiBQYXJzZXMgdGVtcGxhdGUgY2hpbGQgbm9kZXMgZm9yIHRoZSBnaXZlbiByb290IG5vZGUuCiAgICAgICAqCiAgICAgICAqIFRoaXMgbWV0aG9kIGFsc28gd3JhcHMgd2hpdGVsaXN0ZWQgbGVnYWN5IHRlbXBsYXRlIGV4dGVuc2lvbnMKICAgICAgICogKGBpcz0iZG9tLWlmImAgYW5kIGBpcz0iZG9tLXJlcGVhdCJgKSB3aXRoIHRoZWlyIGVxdWl2YWxlbnQgZWxlbWVudAogICAgICAgKiB3cmFwcGVycywgY29sbGFwc2VzIHRleHQgbm9kZXMsIGFuZCBzdHJpcHMgd2hpdGVzcGFjZSBmcm9tIHRoZSB0ZW1wbGF0ZQogICAgICAgKiBpZiB0aGUgYHRlbXBsYXRlSW5mby5zdHJpcFdoaXRlc3BhY2VgIHNldHRpbmcgd2FzIHByb3ZpZGVkLgogICAgICAgKgogICAgICAgKiBAcGFyYW0ge05vZGV9IHJvb3QgUm9vdCBub2RlIHdob3NlIGBjaGlsZE5vZGVzYCB3aWxsIGJlIHBhcnNlZAogICAgICAgKiBAcGFyYW0geyFUZW1wbGF0ZUluZm99IHRlbXBsYXRlSW5mbyBUZW1wbGF0ZSBtZXRhZGF0YSBmb3IgY3VycmVudCB0ZW1wbGF0ZQogICAgICAgKiBAcGFyYW0geyFOb2RlSW5mb30gbm9kZUluZm8gTm9kZSBtZXRhZGF0YSBmb3IgY3VycmVudCB0ZW1wbGF0ZS4KICAgICAgICogQHJldHVybiB7dm9pZH0KICAgICAgICovCiAgICAgIHN0YXRpYyBfcGFyc2VUZW1wbGF0ZUNoaWxkTm9kZXMocm9vdCwgdGVtcGxhdGVJbmZvLCBub2RlSW5mbykgewogICAgICAgIGlmIChyb290LmxvY2FsTmFtZSA9PT0gJ3NjcmlwdCcgfHwgcm9vdC5sb2NhbE5hbWUgPT09ICdzdHlsZScpIHsKICAgICAgICAgIHJldHVybjsKICAgICAgICB9CiAgICAgICAgd2Fsa2VyLmN1cnJlbnROb2RlID0gcm9vdDsKICAgICAgICBmb3IgKGxldCBub2RlPXdhbGtlci5maXJzdENoaWxkKCksIHBhcmVudEluZGV4PTAsIG5leHQ7IG5vZGU7IG5vZGU9bmV4dCkgewogICAgICAgICAgLy8gV3JhcCB0ZW1wbGF0ZXMKICAgICAgICAgIGlmIChub2RlLmxvY2FsTmFtZSA9PSAndGVtcGxhdGUnKSB7CiAgICAgICAgICAgIG5vZGUgPSB3cmFwVGVtcGxhdGVFeHRlbnNpb24obm9kZSk7CiAgICAgICAgICB9CiAgICAgICAgICAvLyBjb2xsYXBzZSBhZGphY2VudCB0ZXh0Tm9kZXM6IGZpeGVzIGFuIElFIGlzc3VlIHRoYXQgY2FuIGNhdXNlCiAgICAgICAgICAvLyB0ZXh0IG5vZGVzIHRvIGJlIGluZXhwbGljYWJseSBzcGxpdCA9KAogICAgICAgICAgLy8gbm90ZSB0aGF0IHJvb3Qubm9ybWFsaXplKCkgc2hvdWxkIHdvcmsgYnV0IGRvZXMgbm90IHNvIHdlIGRvIHRoaXMKICAgICAgICAgIC8vIG1hbnVhbGx5LgogICAgICAgICAgd2Fsa2VyLmN1cnJlbnROb2RlID0gbm9kZTsKICAgICAgICAgIG5leHQgPSB3YWxrZXIubmV4dFNpYmxpbmcoKTsKICAgICAgICAgIGlmIChub2RlLm5vZGVUeXBlID09PSBOb2RlLlRFWFRfTk9ERSkgewogICAgICAgICAgICBsZXQgLyoqIE5vZGUgKi8gbiA9IG5leHQ7CiAgICAgICAgICAgIHdoaWxlIChuICYmIChuLm5vZGVUeXBlID09PSBOb2RlLlRFWFRfTk9ERSkpIHsKICAgICAgICAgICAgICBub2RlLnRleHRDb250ZW50ICs9IG4udGV4dENvbnRlbnQ7CiAgICAgICAgICAgICAgbmV4dCA9IHdhbGtlci5uZXh0U2libGluZygpOwogICAgICAgICAgICAgIHJvb3QucmVtb3ZlQ2hpbGQobik7CiAgICAgICAgICAgICAgbiA9IG5leHQ7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgLy8gb3B0aW9uYWxseSBzdHJpcCB3aGl0ZXNwYWNlCiAgICAgICAgICAgIGlmICh0ZW1wbGF0ZUluZm8uc3RyaXBXaGl0ZVNwYWNlICYmICFub2RlLnRleHRDb250ZW50LnRyaW0oKSkgewogICAgICAgICAgICAgIHJvb3QucmVtb3ZlQ2hpbGQobm9kZSk7CiAgICAgICAgICAgICAgY29udGludWU7CiAgICAgICAgICAgIH0KICAgICAgICAgIH0KICAgICAgICAgIGxldCBjaGlsZEluZm8gPSB7IHBhcmVudEluZGV4LCBwYXJlbnRJbmZvOiBub2RlSW5mbyB9OwogICAgICAgICAgaWYgKHRoaXMuX3BhcnNlVGVtcGxhdGVOb2RlKG5vZGUsIHRlbXBsYXRlSW5mbywgY2hpbGRJbmZvKSkgewogICAgICAgICAgICBjaGlsZEluZm8uaW5mb0luZGV4ID0gdGVtcGxhdGVJbmZvLm5vZGVJbmZvTGlzdC5wdXNoKC8qKiBAdHlwZSB7IU5vZGVJbmZvfSAqLyhjaGlsZEluZm8pKSAtIDE7CiAgICAgICAgICB9CiAgICAgICAgICAvLyBJbmNyZW1lbnQgaWYgbm90IHJlbW92ZWQKICAgICAgICAgIHdhbGtlci5jdXJyZW50Tm9kZSA9IG5vZGU7CiAgICAgICAgICBpZiAod2Fsa2VyLnBhcmVudE5vZGUoKSkgewogICAgICAgICAgICBwYXJlbnRJbmRleCsrOwogICAgICAgICAgfQogICAgICAgIH0KICAgICAgfQoKICAgICAgLyoqCiAgICAgICAqIFBhcnNlcyB0ZW1wbGF0ZSBjb250ZW50IGZvciB0aGUgZ2l2ZW4gbmVzdGVkIGA8dGVtcGxhdGU+YC4KICAgICAgICoKICAgICAgICogTmVzdGVkIHRlbXBsYXRlIGluZm8gaXMgc3RvcmVkIGFzIGB0ZW1wbGF0ZUluZm9gIGluIHRoZSBjdXJyZW50IG5vZGUncwogICAgICAgKiBgbm9kZUluZm9gLiBgdGVtcGxhdGUuY29udGVudGAgaXMgcmVtb3ZlZCBhbmQgc3RvcmVkIGluIGB0ZW1wbGF0ZUluZm9gLgogICAgICAgKiBJdCB3aWxsIHRoZW4gYmUgdGhlIHJlc3BvbnNpYmlsaXR5IG9mIHRoZSBob3N0IHRvIHNldCBpdCBiYWNrIHRvIHRoZQogICAgICAgKiB0ZW1wbGF0ZSBhbmQgZm9yIHVzZXJzIHN0YW1waW5nIG5lc3RlZCB0ZW1wbGF0ZXMgdG8gdXNlIHRoZQogICAgICAgKiBgX2NvbnRlbnRGb3JUZW1wbGF0ZWAgbWV0aG9kIHRvIHJldHJpZXZlIHRoZSBjb250ZW50IGZvciB0aGlzIHRlbXBsYXRlCiAgICAgICAqIChhbiBvcHRpbWl6YXRpb24gdG8gYXZvaWQgdGhlIGNvc3Qgb2YgY2xvbmluZyBuZXN0ZWQgdGVtcGxhdGUgY29udGVudCkuCiAgICAgICAqCiAgICAgICAqIEBwYXJhbSB7SFRNTFRlbXBsYXRlRWxlbWVudH0gbm9kZSBOb2RlIHRvIHBhcnNlIChhIDx0ZW1wbGF0ZT4pCiAgICAgICAqIEBwYXJhbSB7VGVtcGxhdGVJbmZvfSBvdXRlclRlbXBsYXRlSW5mbyBUZW1wbGF0ZSBtZXRhZGF0YSBmb3IgY3VycmVudCB0ZW1wbGF0ZQogICAgICAgKiAgIHRoYXQgaW5jbHVkZXMgdGhlIHRlbXBsYXRlIGBub2RlYAogICAgICAgKiBAcGFyYW0geyFOb2RlSW5mb30gbm9kZUluZm8gTm9kZSBtZXRhZGF0YSBmb3IgY3VycmVudCB0ZW1wbGF0ZS4KICAgICAgICogQHJldHVybiB7Ym9vbGVhbn0gYHRydWVgIGlmIHRoZSB2aXNpdGVkIG5vZGUgYWRkZWQgbm9kZS1zcGVjaWZpYwogICAgICAgKiAgIG1ldGFkYXRhIHRvIGBub2RlSW5mb2AKICAgICAgICovCiAgICAgIHN0YXRpYyBfcGFyc2VUZW1wbGF0ZU5lc3RlZFRlbXBsYXRlKG5vZGUsIG91dGVyVGVtcGxhdGVJbmZvLCBub2RlSW5mbykgewogICAgICAgIGxldCB0ZW1wbGF0ZUluZm8gPSB0aGlzLl9wYXJzZVRlbXBsYXRlKG5vZGUsIG91dGVyVGVtcGxhdGVJbmZvKTsKICAgICAgICBsZXQgY29udGVudCA9IHRlbXBsYXRlSW5mby5jb250ZW50ID0KICAgICAgICAgIG5vZGUuY29udGVudC5vd25lckRvY3VtZW50LmNyZWF0ZURvY3VtZW50RnJhZ21lbnQoKTsKICAgICAgICBjb250ZW50LmFwcGVuZENoaWxkKG5vZGUuY29udGVudCk7CiAgICAgICAgbm9kZUluZm8udGVtcGxhdGVJbmZvID0gdGVtcGxhdGVJbmZvOwogICAgICAgIHJldHVybiB0cnVlOwogICAgICB9CgogICAgICAvKioKICAgICAgICogUGFyc2VzIHRlbXBsYXRlIG5vZGUgYXR0cmlidXRlcyBhbmQgYWRkcyBub2RlIG1ldGFkYXRhIHRvIGBub2RlSW5mb2AKICAgICAgICogZm9yIG5vZGVzIG9mIGludGVyZXN0LgogICAgICAgKgogICAgICAgKiBAcGFyYW0ge0VsZW1lbnR9IG5vZGUgTm9kZSB0byBwYXJzZQogICAgICAgKiBAcGFyYW0ge1RlbXBsYXRlSW5mb30gdGVtcGxhdGVJbmZvIFRlbXBsYXRlIG1ldGFkYXRhIGZvciBjdXJyZW50IHRlbXBsYXRlCiAgICAgICAqIEBwYXJhbSB7Tm9kZUluZm99IG5vZGVJbmZvIE5vZGUgbWV0YWRhdGEgZm9yIGN1cnJlbnQgdGVtcGxhdGUuCiAgICAgICAqIEByZXR1cm4ge2Jvb2xlYW59IGB0cnVlYCBpZiB0aGUgdmlzaXRlZCBub2RlIGFkZGVkIG5vZGUtc3BlY2lmaWMKICAgICAgICogICBtZXRhZGF0YSB0byBgbm9kZUluZm9gCiAgICAgICAqLwogICAgICBzdGF0aWMgX3BhcnNlVGVtcGxhdGVOb2RlQXR0cmlidXRlcyhub2RlLCB0ZW1wbGF0ZUluZm8sIG5vZGVJbmZvKSB7CiAgICAgICAgLy8gTWFrZSBjb3B5IG9mIG9yaWdpbmFsIGF0dHJpYnV0ZSBsaXN0LCBzaW5jZSB0aGUgb3JkZXIgbWF5IGNoYW5nZQogICAgICAgIC8vIGFzIGF0dHJpYnV0ZXMgYXJlIGFkZGVkIGFuZCByZW1vdmVkCiAgICAgICAgbGV0IG5vdGVkID0gZmFsc2U7CiAgICAgICAgbGV0IGF0dHJzID0gQXJyYXkuZnJvbShub2RlLmF0dHJpYnV0ZXMpOwogICAgICAgIGZvciAobGV0IGk9YXR0cnMubGVuZ3RoLTEsIGE7IChhPWF0dHJzW2ldKTsgaS0tKSB7CiAgICAgICAgICBub3RlZCA9IHRoaXMuX3BhcnNlVGVtcGxhdGVOb2RlQXR0cmlidXRlKG5vZGUsIHRlbXBsYXRlSW5mbywgbm9kZUluZm8sIGEubmFtZSwgYS52YWx1ZSkgfHwgbm90ZWQ7CiAgICAgICAgfQogICAgICAgIHJldHVybiBub3RlZDsKICAgICAgfQoKICAgICAgLyoqCiAgICAgICAqIFBhcnNlcyBhIHNpbmdsZSB0ZW1wbGF0ZSBub2RlIGF0dHJpYnV0ZSBhbmQgYWRkcyBub2RlIG1ldGFkYXRhIHRvCiAgICAgICAqIGBub2RlSW5mb2AgZm9yIGF0dHJpYnV0ZXMgb2YgaW50ZXJlc3QuCiAgICAgICAqCiAgICAgICAqIFRoaXMgaW1wbGVtZW50YXRpb24gYWRkcyBtZXRhZGF0YSBmb3IgYG9uLWV2ZW50PSJoYW5kbGVyImAgYXR0cmlidXRlcwogICAgICAgKiBhbmQgYGlkYCBhdHRyaWJ1dGVzLgogICAgICAgKgogICAgICAgKiBAcGFyYW0ge0VsZW1lbnR9IG5vZGUgTm9kZSB0byBwYXJzZQogICAgICAgKiBAcGFyYW0geyFUZW1wbGF0ZUluZm99IHRlbXBsYXRlSW5mbyBUZW1wbGF0ZSBtZXRhZGF0YSBmb3IgY3VycmVudCB0ZW1wbGF0ZQogICAgICAgKiBAcGFyYW0geyFOb2RlSW5mb30gbm9kZUluZm8gTm9kZSBtZXRhZGF0YSBmb3IgY3VycmVudCB0ZW1wbGF0ZS4KICAgICAgICogQHBhcmFtIHtzdHJpbmd9IG5hbWUgQXR0cmlidXRlIG5hbWUKICAgICAgICogQHBhcmFtIHtzdHJpbmd9IHZhbHVlIEF0dHJpYnV0ZSB2YWx1ZQogICAgICAgKiBAcmV0dXJuIHtib29sZWFufSBgdHJ1ZWAgaWYgdGhlIHZpc2l0ZWQgbm9kZSBhZGRlZCBub2RlLXNwZWNpZmljCiAgICAgICAqICAgbWV0YWRhdGEgdG8gYG5vZGVJbmZvYAogICAgICAgKi8KICAgICAgc3RhdGljIF9wYXJzZVRlbXBsYXRlTm9kZUF0dHJpYnV0ZShub2RlLCB0ZW1wbGF0ZUluZm8sIG5vZGVJbmZvLCBuYW1lLCB2YWx1ZSkgewogICAgICAgIC8vIGV2ZW50cyAob24tKikKICAgICAgICBpZiAobmFtZS5zbGljZSgwLCAzKSA9PT0gJ29uLScpIHsKICAgICAgICAgIG5vZGUucmVtb3ZlQXR0cmlidXRlKG5hbWUpOwogICAgICAgICAgbm9kZUluZm8uZXZlbnRzID0gbm9kZUluZm8uZXZlbnRzIHx8IFtdOwogICAgICAgICAgbm9kZUluZm8uZXZlbnRzLnB1c2goewogICAgICAgICAgICBuYW1lOiBuYW1lLnNsaWNlKDMpLAogICAgICAgICAgICB2YWx1ZQogICAgICAgICAgfSk7CiAgICAgICAgICByZXR1cm4gdHJ1ZTsKICAgICAgICB9CiAgICAgICAgLy8gc3RhdGljIGlkCiAgICAgICAgZWxzZSBpZiAobmFtZSA9PT0gJ2lkJykgewogICAgICAgICAgbm9kZUluZm8uaWQgPSB2YWx1ZTsKICAgICAgICAgIHJldHVybiB0cnVlOwogICAgICAgIH0KICAgICAgICByZXR1cm4gZmFsc2U7CiAgICAgIH0KCiAgICAgIC8qKgogICAgICAgKiBSZXR1cm5zIHRoZSBgY29udGVudGAgZG9jdW1lbnQgZnJhZ21lbnQgZm9yIGEgZ2l2ZW4gdGVtcGxhdGUuCiAgICAgICAqCiAgICAgICAqIEZvciBuZXN0ZWQgdGVtcGxhdGVzLCBQb2x5bWVyIHBlcmZvcm1zIGFuIG9wdGltaXphdGlvbiB0byBjYWNoZSBuZXN0ZWQKICAgICAgICogdGVtcGxhdGUgY29udGVudCB0byBhdm9pZCB0aGUgY29zdCBvZiBjbG9uaW5nIGRlZXBseSBuZXN0ZWQgdGVtcGxhdGVzLgogICAgICAgKiBUaGlzIG1ldGhvZCByZXRyaWV2ZXMgdGhlIGNhY2hlZCBjb250ZW50IGZvciBhIGdpdmVuIHRlbXBsYXRlLgogICAgICAgKgogICAgICAgKiBAcGFyYW0ge0hUTUxUZW1wbGF0ZUVsZW1lbnR9IHRlbXBsYXRlIFRlbXBsYXRlIHRvIHJldHJpZXZlIGBjb250ZW50YCBmb3IKICAgICAgICogQHJldHVybiB7RG9jdW1lbnRGcmFnbWVudH0gQ29udGVudCBmcmFnbWVudAogICAgICAgKi8KICAgICAgc3RhdGljIF9jb250ZW50Rm9yVGVtcGxhdGUodGVtcGxhdGUpIHsKICAgICAgICBsZXQgdGVtcGxhdGVJbmZvID0gLyoqIEB0eXBlIHtIVE1MVGVtcGxhdGVFbGVtZW50V2l0aEluZm99ICovICh0ZW1wbGF0ZSkuX3RlbXBsYXRlSW5mbzsKICAgICAgICByZXR1cm4gKHRlbXBsYXRlSW5mbyAmJiB0ZW1wbGF0ZUluZm8uY29udGVudCkgfHwgdGVtcGxhdGUuY29udGVudDsKICAgICAgfQoKICAgICAgLyoqCiAgICAgICAqIENsb25lcyB0aGUgcHJvdmlkZWQgdGVtcGxhdGUgY29udGVudCBhbmQgcmV0dXJucyBhIGRvY3VtZW50IGZyYWdtZW50CiAgICAgICAqIGNvbnRhaW5pbmcgdGhlIGNsb25lZCBkb20uCiAgICAgICAqCiAgICAgICAqIFRoZSB0ZW1wbGF0ZSBpcyBwYXJzZWQgKG9uY2UgYW5kIG1lbW9pemVkKSB1c2luZyB0aGlzIGxpYnJhcnkncwogICAgICAgKiB0ZW1wbGF0ZSBwYXJzaW5nIGZlYXR1cmVzLCBhbmQgcHJvdmlkZXMgdGhlIGZvbGxvd2luZyB2YWx1ZS1hZGRlZAogICAgICAgKiBmZWF0dXJlczoKICAgICAgICogKiBBZGRzIGRlY2xhcmF0aXZlIGV2ZW50IGxpc3RlbmVycyBmb3IgYG9uLWV2ZW50PSJoYW5kbGVyImAgYXR0cmlidXRlcwogICAgICAgKiAqIEdlbmVyYXRlcyBhbiAiaWQgbWFwIiBmb3IgYWxsIG5vZGVzIHdpdGggaWQncyB1bmRlciBgJGAgb24gcmV0dXJuZWQKICAgICAgICogICBkb2N1bWVudCBmcmFnbWVudAogICAgICAgKiAqIFBhc3NlcyB0ZW1wbGF0ZSBpbmZvIGluY2x1ZGluZyBgY29udGVudGAgYmFjayB0byB0ZW1wbGF0ZXMgYXMKICAgICAgICogICBgX3RlbXBsYXRlSW5mb2AgKGEgcGVyZm9ybWFuY2Ugb3B0aW1pemF0aW9uIHRvIGF2b2lkIGRlZXAgdGVtcGxhdGUKICAgICAgICogICBjbG9uaW5nKQogICAgICAgKgogICAgICAgKiBOb3RlIHRoYXQgdGhlIG1lbW9pemVkIHRlbXBsYXRlIHBhcnNpbmcgcHJvY2VzcyBpcyBkZXN0cnVjdGl2ZSB0byB0aGUKICAgICAgICogdGVtcGxhdGU6IGF0dHJpYnV0ZXMgZm9yIGJpbmRpbmdzIGFuZCBkZWNsYXJhdGl2ZSBldmVudCBsaXN0ZW5lcnMgYXJlCiAgICAgICAqIHJlbW92ZWQgYWZ0ZXIgYmVpbmcgbm90ZWQgaW4gbm90ZXMsIGFuZCBhbnkgbmVzdGVkIGA8dGVtcGxhdGU+LmNvbnRlbnRgCiAgICAgICAqIGlzIHJlbW92ZWQgYW5kIHN0b3JlZCBpbiBub3RlcyBhcyB3ZWxsLgogICAgICAgKgogICAgICAgKiBAcGFyYW0geyFIVE1MVGVtcGxhdGVFbGVtZW50fSB0ZW1wbGF0ZSBUZW1wbGF0ZSB0byBzdGFtcAogICAgICAgKiBAcmV0dXJuIHshU3RhbXBlZFRlbXBsYXRlfSBDbG9uZWQgdGVtcGxhdGUgY29udGVudAogICAgICAgKi8KICAgICAgX3N0YW1wVGVtcGxhdGUodGVtcGxhdGUpIHsKICAgICAgICAvLyBQb2x5ZmlsbCBzdXBwb3J0OiBib290c3RyYXAgdGhlIHRlbXBsYXRlIGlmIGl0IGhhcyBub3QgYWxyZWFkeSBiZWVuCiAgICAgICAgaWYgKHRlbXBsYXRlICYmICF0ZW1wbGF0ZS5jb250ZW50ICYmCiAgICAgICAgICAgIHdpbmRvdy5IVE1MVGVtcGxhdGVFbGVtZW50ICYmIEhUTUxUZW1wbGF0ZUVsZW1lbnQuZGVjb3JhdGUpIHsKICAgICAgICAgIEhUTUxUZW1wbGF0ZUVsZW1lbnQuZGVjb3JhdGUodGVtcGxhdGUpOwogICAgICAgIH0KICAgICAgICBsZXQgdGVtcGxhdGVJbmZvID0gdGhpcy5jb25zdHJ1Y3Rvci5fcGFyc2VUZW1wbGF0ZSh0ZW1wbGF0ZSk7CiAgICAgICAgbGV0IG5vZGVJbmZvID0gdGVtcGxhdGVJbmZvLm5vZGVJbmZvTGlzdDsKICAgICAgICBsZXQgY29udGVudCA9IHRlbXBsYXRlSW5mby5jb250ZW50IHx8IHRlbXBsYXRlLmNvbnRlbnQ7CiAgICAgICAgbGV0IGRvbSA9IC8qKiBAdHlwZSB7RG9jdW1lbnRGcmFnbWVudH0gKi8gKGRvY3VtZW50LmltcG9ydE5vZGUoY29udGVudCwgdHJ1ZSkpOwogICAgICAgIC8vIE5PVEU6IFNoYWR5RG9tIG9wdGltaXphdGlvbiBpbmRpY2F0aW5nIHRoZXJlIGlzIGFuIGluc2VydGlvbiBwb2ludAogICAgICAgIGRvbS5fX25vSW5zZXJ0aW9uUG9pbnQgPSAhdGVtcGxhdGVJbmZvLmhhc0luc2VydGlvblBvaW50OwogICAgICAgIGxldCBub2RlcyA9IGRvbS5ub2RlTGlzdCA9IG5ldyBBcnJheShub2RlSW5mby5sZW5ndGgpOwogICAgICAgIGRvbS4kID0ge307CiAgICAgICAgZm9yIChsZXQgaT0wLCBsPW5vZGVJbmZvLmxlbmd0aCwgaW5mbzsgKGk8bCkgJiYgKGluZm89bm9kZUluZm9baV0pOyBpKyspIHsKICAgICAgICAgIGxldCBub2RlID0gbm9kZXNbaV0gPSBmaW5kVGVtcGxhdGVOb2RlKGRvbSwgaW5mbyk7CiAgICAgICAgICBhcHBseUlkVG9NYXAodGhpcywgZG9tLiQsIG5vZGUsIGluZm8pOwogICAgICAgICAgYXBwbHlUZW1wbGF0ZUNvbnRlbnQodGhpcywgbm9kZSwgaW5mbyk7CiAgICAgICAgICBhcHBseUV2ZW50TGlzdGVuZXIodGhpcywgbm9kZSwgaW5mbyk7CiAgICAgICAgfQogICAgICAgIGRvbSA9IC8qKiBAdHlwZSB7IVN0YW1wZWRUZW1wbGF0ZX0gKi8oZG9tKTsgLy8gZXNsaW50LWRpc2FibGUtbGluZSBuby1zZWxmLWFzc2lnbgogICAgICAgIHJldHVybiBkb207CiAgICAgIH0KCiAgICAgIC8qKgogICAgICAgKiBBZGRzIGFuIGV2ZW50IGxpc3RlbmVyIGJ5IG1ldGhvZCBuYW1lIGZvciB0aGUgZXZlbnQgcHJvdmlkZWQuCiAgICAgICAqCiAgICAgICAqIFRoaXMgbWV0aG9kIGdlbmVyYXRlcyBhIGhhbmRsZXIgZnVuY3Rpb24gdGhhdCBsb29rcyB1cCB0aGUgbWV0aG9kCiAgICAgICAqIG5hbWUgYXQgaGFuZGxpbmcgdGltZS4KICAgICAgICoKICAgICAgICogQHBhcmFtIHshTm9kZX0gbm9kZSBOb2RlIHRvIGFkZCBsaXN0ZW5lciBvbgogICAgICAgKiBAcGFyYW0ge3N0cmluZ30gZXZlbnROYW1lIE5hbWUgb2YgZXZlbnQKICAgICAgICogQHBhcmFtIHtzdHJpbmd9IG1ldGhvZE5hbWUgTmFtZSBvZiBtZXRob2QKICAgICAgICogQHBhcmFtIHsqPX0gY29udGV4dCBDb250ZXh0IHRoZSBtZXRob2Qgd2lsbCBiZSBjYWxsZWQgb24gKGRlZmF1bHRzCiAgICAgICAqICAgdG8gYG5vZGVgKQogICAgICAgKiBAcmV0dXJuIHtGdW5jdGlvbn0gR2VuZXJhdGVkIGhhbmRsZXIgZnVuY3Rpb24KICAgICAgICovCiAgICAgIF9hZGRNZXRob2RFdmVudExpc3RlbmVyVG9Ob2RlKG5vZGUsIGV2ZW50TmFtZSwgbWV0aG9kTmFtZSwgY29udGV4dCkgewogICAgICAgIGNvbnRleHQgPSBjb250ZXh0IHx8IG5vZGU7CiAgICAgICAgbGV0IGhhbmRsZXIgPSBjcmVhdGVOb2RlRXZlbnRIYW5kbGVyKGNvbnRleHQsIGV2ZW50TmFtZSwgbWV0aG9kTmFtZSk7CiAgICAgICAgdGhpcy5fYWRkRXZlbnRMaXN0ZW5lclRvTm9kZShub2RlLCBldmVudE5hbWUsIGhhbmRsZXIpOwogICAgICAgIHJldHVybiBoYW5kbGVyOwogICAgICB9CgogICAgICAvKioKICAgICAgICogT3ZlcnJpZGUgcG9pbnQgZm9yIGFkZGluZyBjdXN0b20gb3Igc2ltdWxhdGVkIGV2ZW50IGhhbmRsaW5nLgogICAgICAgKgogICAgICAgKiBAcGFyYW0geyFOb2RlfSBub2RlIE5vZGUgdG8gYWRkIGV2ZW50IGxpc3RlbmVyIHRvCiAgICAgICAqIEBwYXJhbSB7c3RyaW5nfSBldmVudE5hbWUgTmFtZSBvZiBldmVudAogICAgICAgKiBAcGFyYW0ge2Z1bmN0aW9uKCFFdmVudCk6dm9pZH0gaGFuZGxlciBMaXN0ZW5lciBmdW5jdGlvbiB0byBhZGQKICAgICAgICogQHJldHVybiB7dm9pZH0KICAgICAgICovCiAgICAgIF9hZGRFdmVudExpc3RlbmVyVG9Ob2RlKG5vZGUsIGV2ZW50TmFtZSwgaGFuZGxlcikgewogICAgICAgIG5vZGUuYWRkRXZlbnRMaXN0ZW5lcihldmVudE5hbWUsIGhhbmRsZXIpOwogICAgICB9CgogICAgICAvKioKICAgICAgICogT3ZlcnJpZGUgcG9pbnQgZm9yIGFkZGluZyBjdXN0b20gb3Igc2ltdWxhdGVkIGV2ZW50IGhhbmRsaW5nLgogICAgICAgKgogICAgICAgKiBAcGFyYW0geyFOb2RlfSBub2RlIE5vZGUgdG8gcmVtb3ZlIGV2ZW50IGxpc3RlbmVyIGZyb20KICAgICAgICogQHBhcmFtIHtzdHJpbmd9IGV2ZW50TmFtZSBOYW1lIG9mIGV2ZW50CiAgICAgICAqIEBwYXJhbSB7ZnVuY3Rpb24oIUV2ZW50KTp2b2lkfSBoYW5kbGVyIExpc3RlbmVyIGZ1bmN0aW9uIHRvIHJlbW92ZQogICAgICAgKiBAcmV0dXJuIHt2b2lkfQogICAgICAgKi8KICAgICAgX3JlbW92ZUV2ZW50TGlzdGVuZXJGcm9tTm9kZShub2RlLCBldmVudE5hbWUsIGhhbmRsZXIpIHsKICAgICAgICBub2RlLnJlbW92ZUV2ZW50TGlzdGVuZXIoZXZlbnROYW1lLCBoYW5kbGVyKTsKICAgICAgfQoKICAgIH0KCiAgICByZXR1cm4gVGVtcGxhdGVTdGFtcDsKCiAgfSk7Cgp9KSgpOwo8L3NjcmlwdD48c2NyaXB0PgooZnVuY3Rpb24oKSB7CgogICd1c2Ugc3RyaWN0JzsKCiAgLyoqIEBjb25zdCB7T2JqZWN0fSAqLwogIGNvbnN0IENhc2VNYXAgPSBQb2x5bWVyLkNhc2VNYXA7CgogIC8vIE1vbm90b25pY2FsbHkgaW5jcmVhc2luZyB1bmlxdWUgSUQgdXNlZCBmb3IgZGUtZHVwaW5nIGVmZmVjdHMgdHJpZ2dlcmVkCiAgLy8gZnJvbSBtdWx0aXBsZSBwcm9wZXJ0aWVzIGluIHRoZSBzYW1lIHR1cm4KICBsZXQgZGVkdXBlSWQgPSAwOwoKICAvKioKICAgKiBQcm9wZXJ0eSBlZmZlY3QgdHlwZXM7IGVmZmVjdHMgYXJlIHN0b3JlZCBvbiB0aGUgcHJvdG90eXBlIHVzaW5nIHRoZXNlIGtleXMKICAgKiBAZW51bSB7c3RyaW5nfQogICAqLwogIGNvbnN0IFRZUEVTID0gewogICAgQ09NUFVURTogJ19fY29tcHV0ZUVmZmVjdHMnLAogICAgUkVGTEVDVDogJ19fcmVmbGVjdEVmZmVjdHMnLAogICAgTk9USUZZOiAnX19ub3RpZnlFZmZlY3RzJywKICAgIFBST1BBR0FURTogJ19fcHJvcGFnYXRlRWZmZWN0cycsCiAgICBPQlNFUlZFOiAnX19vYnNlcnZlRWZmZWN0cycsCiAgICBSRUFEX09OTFk6ICdfX3JlYWRPbmx5JwogIH07CgogIC8qKiBAY29uc3Qge1JlZ0V4cH0gKi8KICBjb25zdCBjYXBpdGFsQXR0cmlidXRlUmVnZXggPSAvW0EtWl0vOwoKICAvKioKICAgKiBAdHlwZWRlZiB7ewogICAqIG5hbWU6IChzdHJpbmcgfCB1bmRlZmluZWQpLAogICAqIHN0cnVjdHVyZWQ6IChib29sZWFuIHwgdW5kZWZpbmVkKSwKICAgKiB3aWxkY2FyZDogKGJvb2xlYW4gfCB1bmRlZmluZWQpCiAgICogfX0KICAgKi8KICBsZXQgRGF0YVRyaWdnZXI7IC8vZXNsaW50LWRpc2FibGUtbGluZSBuby11bnVzZWQtdmFycwoKICAvKioKICAgKiBAdHlwZWRlZiB7ewogICAqIGluZm86ID8sCiAgICogdHJpZ2dlcjogKCFEYXRhVHJpZ2dlciB8IHVuZGVmaW5lZCksCiAgICogZm46ICghRnVuY3Rpb24gfCB1bmRlZmluZWQpCiAgICogfX0KICAgKi8KICBsZXQgRGF0YUVmZmVjdDsgLy9lc2xpbnQtZGlzYWJsZS1saW5lIG5vLXVudXNlZC12YXJzCgogIGxldCBQcm9wZXJ0eUVmZmVjdHNUeXBlOyAvL2VzbGludC1kaXNhYmxlLWxpbmUgbm8tdW51c2VkLXZhcnMKCiAgLyoqCiAgICogRW5zdXJlcyB0aGF0IHRoZSBtb2RlbCBoYXMgYW4gb3duLXByb3BlcnR5IG1hcCBvZiBlZmZlY3RzIGZvciB0aGUgZ2l2ZW4gdHlwZS4KICAgKiBUaGUgbW9kZWwgbWF5IGJlIGEgcHJvdG90eXBlIG9yIGFuIGluc3RhbmNlLgogICAqCiAgICogUHJvcGVydHkgZWZmZWN0cyBhcmUgc3RvcmVkIGFzIGFycmF5cyBvZiBlZmZlY3RzIGJ5IHByb3BlcnR5IGluIGEgbWFwLAogICAqIGJ5IG5hbWVkIHR5cGUgb24gdGhlIG1vZGVsLiBlLmcuCiAgICoKICAgKiAgIF9fY29tcHV0ZUVmZmVjdHM6IHsKICAgKiAgICAgZm9vOiBbIC4uLiBdLAogICAqICAgICBiYXI6IFsgLi4uIF0KICAgKiAgIH0KICAgKgogICAqIElmIHRoZSBtb2RlbCBkb2VzIG5vdCB5ZXQgaGF2ZSBhbiBlZmZlY3QgbWFwIGZvciB0aGUgdHlwZSwgb25lIGlzIGNyZWF0ZWQKICAgKiBhbmQgcmV0dXJuZWQuICBJZiBpdCBkb2VzLCBidXQgaXQgaXMgbm90IGFuIG93biBwcm9wZXJ0eSAoaS5lLiB0aGUKICAgKiBwcm90b3R5cGUgaGFkIGVmZmVjdHMpLCB0aGUgdGhlIG1hcCBpcyBkZWVwbHkgY2xvbmVkIGFuZCB0aGUgY29weSBpcwogICAqIHNldCBvbiB0aGUgbW9kZWwgYW5kIHJldHVybmVkLCByZWFkeSBmb3IgbmV3IGVmZmVjdHMgdG8gYmUgYWRkZWQuCiAgICoKICAgKiBAcGFyYW0ge09iamVjdH0gbW9kZWwgUHJvdG90eXBlIG9yIGluc3RhbmNlCiAgICogQHBhcmFtIHtzdHJpbmd9IHR5cGUgUHJvcGVydHkgZWZmZWN0IHR5cGUKICAgKiBAcmV0dXJuIHtPYmplY3R9IFRoZSBvd24tcHJvcGVydHkgbWFwIG9mIGVmZmVjdHMgZm9yIHRoZSBnaXZlbiB0eXBlCiAgICogQHByaXZhdGUKICAgKi8KICBmdW5jdGlvbiBlbnN1cmVPd25FZmZlY3RNYXAobW9kZWwsIHR5cGUpIHsKICAgIGxldCBlZmZlY3RzID0gbW9kZWxbdHlwZV07CiAgICBpZiAoIWVmZmVjdHMpIHsKICAgICAgZWZmZWN0cyA9IG1vZGVsW3R5cGVdID0ge307CiAgICB9IGVsc2UgaWYgKCFtb2RlbC5oYXNPd25Qcm9wZXJ0eSh0eXBlKSkgewogICAgICBlZmZlY3RzID0gbW9kZWxbdHlwZV0gPSBPYmplY3QuY3JlYXRlKG1vZGVsW3R5cGVdKTsKICAgICAgZm9yIChsZXQgcCBpbiBlZmZlY3RzKSB7CiAgICAgICAgbGV0IHByb3RvRnggPSBlZmZlY3RzW3BdOwogICAgICAgIGxldCBpbnN0RnggPSBlZmZlY3RzW3BdID0gQXJyYXkocHJvdG9GeC5sZW5ndGgpOwogICAgICAgIGZvciAobGV0IGk9MDsgaTxwcm90b0Z4Lmxlbmd0aDsgaSsrKSB7CiAgICAgICAgICBpbnN0RnhbaV0gPSBwcm90b0Z4W2ldOwogICAgICAgIH0KICAgICAgfQogICAgfQogICAgcmV0dXJuIGVmZmVjdHM7CiAgfQoKICAvLyAtLSBlZmZlY3RzIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiAgLyoqCiAgICogUnVucyBhbGwgZWZmZWN0cyBvZiBhIGdpdmVuIHR5cGUgZm9yIHRoZSBnaXZlbiBzZXQgb2YgcHJvcGVydHkgY2hhbmdlcwogICAqIG9uIGFuIGluc3RhbmNlLgogICAqCiAgICogQHBhcmFtIHshUHJvcGVydHlFZmZlY3RzVHlwZX0gaW5zdCBUaGUgaW5zdGFuY2Ugd2l0aCBlZmZlY3RzIHRvIHJ1bgogICAqIEBwYXJhbSB7T2JqZWN0fSBlZmZlY3RzIE9iamVjdCBtYXAgb2YgcHJvcGVydHktdG8tQXJyYXkgb2YgZWZmZWN0cwogICAqIEBwYXJhbSB7T2JqZWN0fSBwcm9wcyBCYWcgb2YgY3VycmVudCBwcm9wZXJ0eSBjaGFuZ2VzCiAgICogQHBhcmFtIHtPYmplY3Q9fSBvbGRQcm9wcyBCYWcgb2YgcHJldmlvdXMgdmFsdWVzIGZvciBjaGFuZ2VkIHByb3BlcnRpZXMKICAgKiBAcGFyYW0ge2Jvb2xlYW49fSBoYXNQYXRocyBUcnVlIHdpdGggYHByb3BzYCBjb250YWlucyBvbmUgb3IgbW9yZSBwYXRocwogICAqIEBwYXJhbSB7Kj19IGV4dHJhQXJncyBBZGRpdGlvbmFsIG1ldGFkYXRhIHRvIHBhc3MgdG8gZWZmZWN0IGZ1bmN0aW9uCiAgICogQHJldHVybiB7Ym9vbGVhbn0gVHJ1ZSBpZiBhbiBlZmZlY3QgcmFuIGZvciB0aGlzIHByb3BlcnR5CiAgICogQHByaXZhdGUKICAgKi8KICBmdW5jdGlvbiBydW5FZmZlY3RzKGluc3QsIGVmZmVjdHMsIHByb3BzLCBvbGRQcm9wcywgaGFzUGF0aHMsIGV4dHJhQXJncykgewogICAgaWYgKGVmZmVjdHMpIHsKICAgICAgbGV0IHJhbiA9IGZhbHNlOwogICAgICBsZXQgaWQgPSBkZWR1cGVJZCsrOwogICAgICBmb3IgKGxldCBwcm9wIGluIHByb3BzKSB7CiAgICAgICAgaWYgKHJ1bkVmZmVjdHNGb3JQcm9wZXJ0eShpbnN0LCBlZmZlY3RzLCBpZCwgcHJvcCwgcHJvcHMsIG9sZFByb3BzLCBoYXNQYXRocywgZXh0cmFBcmdzKSkgewogICAgICAgICAgcmFuID0gdHJ1ZTsKICAgICAgICB9CiAgICAgIH0KICAgICAgcmV0dXJuIHJhbjsKICAgIH0KICAgIHJldHVybiBmYWxzZTsKICB9CgogIC8qKgogICAqIFJ1bnMgYSBsaXN0IG9mIGVmZmVjdHMgZm9yIGEgZ2l2ZW4gcHJvcGVydHkuCiAgICoKICAgKiBAcGFyYW0geyFQcm9wZXJ0eUVmZmVjdHNUeXBlfSBpbnN0IFRoZSBpbnN0YW5jZSB3aXRoIGVmZmVjdHMgdG8gcnVuCiAgICogQHBhcmFtIHtPYmplY3R9IGVmZmVjdHMgT2JqZWN0IG1hcCBvZiBwcm9wZXJ0eS10by1BcnJheSBvZiBlZmZlY3RzCiAgICogQHBhcmFtIHtudW1iZXJ9IGRlZHVwZUlkIENvdW50ZXIgdXNlZCBmb3IgZGUtZHVwaW5nIGVmZmVjdHMKICAgKiBAcGFyYW0ge3N0cmluZ30gcHJvcCBOYW1lIG9mIGNoYW5nZWQgcHJvcGVydHkKICAgKiBAcGFyYW0geyp9IHByb3BzIENoYW5nZWQgcHJvcGVydGllcwogICAqIEBwYXJhbSB7Kn0gb2xkUHJvcHMgT2xkIHByb3BlcnRpZXMKICAgKiBAcGFyYW0ge2Jvb2xlYW49fSBoYXNQYXRocyBUcnVlIHdpdGggYHByb3BzYCBjb250YWlucyBvbmUgb3IgbW9yZSBwYXRocwogICAqIEBwYXJhbSB7Kj19IGV4dHJhQXJncyBBZGRpdGlvbmFsIG1ldGFkYXRhIHRvIHBhc3MgdG8gZWZmZWN0IGZ1bmN0aW9uCiAgICogQHJldHVybiB7Ym9vbGVhbn0gVHJ1ZSBpZiBhbiBlZmZlY3QgcmFuIGZvciB0aGlzIHByb3BlcnR5CiAgICogQHByaXZhdGUKICAgKi8KICBmdW5jdGlvbiBydW5FZmZlY3RzRm9yUHJvcGVydHkoaW5zdCwgZWZmZWN0cywgZGVkdXBlSWQsIHByb3AsIHByb3BzLCBvbGRQcm9wcywgaGFzUGF0aHMsIGV4dHJhQXJncykgewogICAgbGV0IHJhbiA9IGZhbHNlOwogICAgbGV0IHJvb3RQcm9wZXJ0eSA9IGhhc1BhdGhzID8gUG9seW1lci5QYXRoLnJvb3QocHJvcCkgOiBwcm9wOwogICAgbGV0IGZ4cyA9IGVmZmVjdHNbcm9vdFByb3BlcnR5XTsKICAgIGlmIChmeHMpIHsKICAgICAgZm9yIChsZXQgaT0wLCBsPWZ4cy5sZW5ndGgsIGZ4OyAoaTxsKSAmJiAoZng9ZnhzW2ldKTsgaSsrKSB7CiAgICAgICAgaWYgKCghZnguaW5mbyB8fCBmeC5pbmZvLmxhc3RSdW4gIT09IGRlZHVwZUlkKSAmJgogICAgICAgICAgICAoIWhhc1BhdGhzIHx8IHBhdGhNYXRjaGVzVHJpZ2dlcihwcm9wLCBmeC50cmlnZ2VyKSkpIHsKICAgICAgICAgIGlmIChmeC5pbmZvKSB7CiAgICAgICAgICAgIGZ4LmluZm8ubGFzdFJ1biA9IGRlZHVwZUlkOwogICAgICAgICAgfQogICAgICAgICAgZnguZm4oaW5zdCwgcHJvcCwgcHJvcHMsIG9sZFByb3BzLCBmeC5pbmZvLCBoYXNQYXRocywgZXh0cmFBcmdzKTsKICAgICAgICAgIHJhbiA9IHRydWU7CiAgICAgICAgfQogICAgICB9CiAgICB9CiAgICByZXR1cm4gcmFuOwogIH0KCiAgLyoqCiAgICogRGV0ZXJtaW5lcyB3aGV0aGVyIGEgcHJvcGVydHkvcGF0aCB0aGF0IGhhcyBjaGFuZ2VkIG1hdGNoZXMgdGhlIHRyaWdnZXIKICAgKiBjcml0ZXJpYSBmb3IgYW4gZWZmZWN0LiAgQSB0cmlnZ2VyIGlzIGEgZGVzY3JpcHRvciB3aXRoIHRoZSBmb2xsb3dpbmcKICAgKiBzdHJ1Y3R1cmUsIHdoaWNoIG1hdGNoZXMgdGhlIGRlc2NyaXB0b3JzIHJldHVybmVkIGZyb20gYHBhcnNlQXJnYC4KICAgKiBlLmcuIGZvciBgZm9vLmJhci4qYDoKICAgKiBgYGAKICAgKiB0cmlnZ2VyOiB7CiAgICogICBuYW1lOiAnYS5iJywKICAgKiAgIHN0cnVjdHVyZWQ6IHRydWUsCiAgICogICB3aWxkY2FyZDogdHJ1ZQogICAqIH0KICAgKiBgYGAKICAgKiBJZiBubyB0cmlnZ2VyIGlzIGdpdmVuLCB0aGUgcGF0aCBpcyBkZWVtZWQgdG8gbWF0Y2guCiAgICoKICAgKiBAcGFyYW0ge3N0cmluZ30gcGF0aCBQYXRoIG9yIHByb3BlcnR5IHRoYXQgY2hhbmdlZAogICAqIEBwYXJhbSB7RGF0YVRyaWdnZXJ9IHRyaWdnZXIgRGVzY3JpcHRvcgogICAqIEByZXR1cm4ge2Jvb2xlYW59IFdoZXRoZXIgdGhlIHBhdGggbWF0Y2hlZCB0aGUgdHJpZ2dlcgogICAqLwogIGZ1bmN0aW9uIHBhdGhNYXRjaGVzVHJpZ2dlcihwYXRoLCB0cmlnZ2VyKSB7CiAgICBpZiAodHJpZ2dlcikgewogICAgICBsZXQgdHJpZ2dlclBhdGggPSB0cmlnZ2VyLm5hbWU7CiAgICAgIHJldHVybiAodHJpZ2dlclBhdGggPT0gcGF0aCkgfHwKICAgICAgICAodHJpZ2dlci5zdHJ1Y3R1cmVkICYmIFBvbHltZXIuUGF0aC5pc0FuY2VzdG9yKHRyaWdnZXJQYXRoLCBwYXRoKSkgfHwKICAgICAgICAodHJpZ2dlci53aWxkY2FyZCAmJiBQb2x5bWVyLlBhdGguaXNEZXNjZW5kYW50KHRyaWdnZXJQYXRoLCBwYXRoKSk7CiAgICB9IGVsc2UgewogICAgICByZXR1cm4gdHJ1ZTsKICAgIH0KICB9CgogIC8qKgogICAqIEltcGxlbWVudHMgdGhlICJvYnNlcnZlciIgZWZmZWN0LgogICAqCiAgICogQ2FsbHMgdGhlIG1ldGhvZCB3aXRoIGBpbmZvLm1ldGhvZE5hbWVgIG9uIHRoZSBpbnN0YW5jZSwgcGFzc2luZyB0aGUKICAgKiBuZXcgYW5kIG9sZCB2YWx1ZXMuCiAgICoKICAgKiBAcGFyYW0geyFQcm9wZXJ0eUVmZmVjdHNUeXBlfSBpbnN0IFRoZSBpbnN0YW5jZSB0aGUgZWZmZWN0IHdpbGwgYmUgcnVuIG9uCiAgICogQHBhcmFtIHtzdHJpbmd9IHByb3BlcnR5IE5hbWUgb2YgcHJvcGVydHkKICAgKiBAcGFyYW0ge09iamVjdH0gcHJvcHMgQmFnIG9mIGN1cnJlbnQgcHJvcGVydHkgY2hhbmdlcwogICAqIEBwYXJhbSB7T2JqZWN0fSBvbGRQcm9wcyBCYWcgb2YgcHJldmlvdXMgdmFsdWVzIGZvciBjaGFuZ2VkIHByb3BlcnRpZXMKICAgKiBAcGFyYW0gez99IGluZm8gRWZmZWN0IG1ldGFkYXRhCiAgICogQHJldHVybiB7dm9pZH0KICAgKiBAcHJpdmF0ZQogICAqLwogIGZ1bmN0aW9uIHJ1bk9ic2VydmVyRWZmZWN0KGluc3QsIHByb3BlcnR5LCBwcm9wcywgb2xkUHJvcHMsIGluZm8pIHsKICAgIGxldCBmbiA9IHR5cGVvZiBpbmZvLm1ldGhvZCA9PT0gInN0cmluZyIgPyBpbnN0W2luZm8ubWV0aG9kXSA6IGluZm8ubWV0aG9kOwogICAgbGV0IGNoYW5nZWRQcm9wID0gaW5mby5wcm9wZXJ0eTsKICAgIGlmIChmbikgewogICAgICBmbi5jYWxsKGluc3QsIGluc3QuX19kYXRhW2NoYW5nZWRQcm9wXSwgb2xkUHJvcHNbY2hhbmdlZFByb3BdKTsKICAgIH0gZWxzZSBpZiAoIWluZm8uZHluYW1pY0ZuKSB7CiAgICAgIGNvbnNvbGUud2Fybignb2JzZXJ2ZXIgbWV0aG9kIGAnICsgaW5mby5tZXRob2QgKyAnYCBub3QgZGVmaW5lZCcpOwogICAgfQogIH0KCiAgLyoqCiAgICogUnVucyAibm90aWZ5IiBlZmZlY3RzIGZvciBhIHNldCBvZiBjaGFuZ2VkIHByb3BlcnRpZXMuCiAgICoKICAgKiBUaGlzIG1ldGhvZCBkaWZmZXJzIGZyb20gdGhlIGdlbmVyaWMgYHJ1bkVmZmVjdHNgIG1ldGhvZCBpbiB0aGF0IGl0CiAgICogd2lsbCBkaXNwYXRjaCBwYXRoIG5vdGlmaWNhdGlvbiBldmVudHMgaW4gdGhlIGNhc2UgdGhhdCB0aGUgcHJvcGVydHkKICAgKiBjaGFuZ2VkIHdhcyBhIHBhdGggYW5kIHRoZSByb290IHByb3BlcnR5IGZvciB0aGF0IHBhdGggZGlkbid0IGhhdmUgYQogICAqICJub3RpZnkiIGVmZmVjdC4gIFRoaXMgaXMgdG8gbWFpbnRhaW4gMS4wIGJlaGF2aW9yIHRoYXQgZGlkIG5vdCByZXF1aXJlCiAgICogYG5vdGlmeTogdHJ1ZWAgdG8gZW5zdXJlIG9iamVjdCBzdWItcHJvcGVydHkgbm90aWZpY2F0aW9ucyB3ZXJlCiAgICogc2VudC4KICAgKgogICAqIEBwYXJhbSB7IVByb3BlcnR5RWZmZWN0c1R5cGV9IGluc3QgVGhlIGluc3RhbmNlIHdpdGggZWZmZWN0cyB0byBydW4KICAgKiBAcGFyYW0ge09iamVjdH0gbm90aWZ5UHJvcHMgQmFnIG9mIHByb3BlcnRpZXMgdG8gbm90aWZ5CiAgICogQHBhcmFtIHtPYmplY3R9IHByb3BzIEJhZyBvZiBjdXJyZW50IHByb3BlcnR5IGNoYW5nZXMKICAgKiBAcGFyYW0ge09iamVjdH0gb2xkUHJvcHMgQmFnIG9mIHByZXZpb3VzIHZhbHVlcyBmb3IgY2hhbmdlZCBwcm9wZXJ0aWVzCiAgICogQHBhcmFtIHtib29sZWFufSBoYXNQYXRocyBUcnVlIHdpdGggYHByb3BzYCBjb250YWlucyBvbmUgb3IgbW9yZSBwYXRocwogICAqIEByZXR1cm4ge3ZvaWR9CiAgICogQHByaXZhdGUKICAgKi8KICBmdW5jdGlvbiBydW5Ob3RpZnlFZmZlY3RzKGluc3QsIG5vdGlmeVByb3BzLCBwcm9wcywgb2xkUHJvcHMsIGhhc1BhdGhzKSB7CiAgICAvLyBOb3RpZnkKICAgIGxldCBmeHMgPSBpbnN0W1RZUEVTLk5PVElGWV07CiAgICBsZXQgbm90aWZpZWQ7CiAgICBsZXQgaWQgPSBkZWR1cGVJZCsrOwogICAgLy8gVHJ5IG5vcm1hbCBub3RpZnkgZWZmZWN0czsgaWYgbm9uZSwgZmFsbCBiYWNrIHRvIHRyeSBwYXRoIG5vdGlmaWNhdGlvbgogICAgZm9yIChsZXQgcHJvcCBpbiBub3RpZnlQcm9wcykgewogICAgICBpZiAobm90aWZ5UHJvcHNbcHJvcF0pIHsKICAgICAgICBpZiAoZnhzICYmIHJ1bkVmZmVjdHNGb3JQcm9wZXJ0eShpbnN0LCBmeHMsIGlkLCBwcm9wLCBwcm9wcywgb2xkUHJvcHMsIGhhc1BhdGhzKSkgewogICAgICAgICAgbm90aWZpZWQgPSB0cnVlOwogICAgICAgIH0gZWxzZSBpZiAoaGFzUGF0aHMgJiYgbm90aWZ5UGF0aChpbnN0LCBwcm9wLCBwcm9wcykpIHsKICAgICAgICAgIG5vdGlmaWVkID0gdHJ1ZTsKICAgICAgICB9CiAgICAgIH0KICAgIH0KICAgIC8vIEZsdXNoIGhvc3QgaWYgd2UgYWN0dWFsbHkgbm90aWZpZWQgYW5kIGhvc3Qgd2FzIGJhdGNoaW5nCiAgICAvLyBBbmQgdGhlIGhvc3QgaGFzIGFscmVhZHkgaW5pdGlhbGl6ZWQgY2xpZW50czsgdGhpcyBwcmV2ZW50cwogICAgLy8gYW4gaXNzdWUgd2l0aCBhIGhvc3Qgb2JzZXJ2aW5nIGRhdGEgY2hhbmdlcyBiZWZvcmUgY2xpZW50cyBhcmUgcmVhZHkuCiAgICBsZXQgaG9zdDsKICAgIGlmIChub3RpZmllZCAmJiAoaG9zdCA9IGluc3QuX19kYXRhSG9zdCkgJiYgaG9zdC5faW52YWxpZGF0ZVByb3BlcnRpZXMpIHsKICAgICAgaG9zdC5faW52YWxpZGF0ZVByb3BlcnRpZXMoKTsKICAgIH0KICB9CgogIC8qKgogICAqIERpc3BhdGNoZXMge3Byb3BlcnR5fS1jaGFuZ2VkIGV2ZW50cyB3aXRoIHBhdGggaW5mb3JtYXRpb24gaW4gdGhlIGRldGFpbAogICAqIG9iamVjdCB0byBpbmRpY2F0ZSBhIHN1Yi1wYXRoIG9mIHRoZSBwcm9wZXJ0eSB3YXMgY2hhbmdlZC4KICAgKgogICAqIEBwYXJhbSB7IVByb3BlcnR5RWZmZWN0c1R5cGV9IGluc3QgVGhlIGVsZW1lbnQgZnJvbSB3aGljaCB0byBmaXJlIHRoZSBldmVudAogICAqIEBwYXJhbSB7c3RyaW5nfSBwYXRoIFRoZSBwYXRoIHRoYXQgd2FzIGNoYW5nZWQKICAgKiBAcGFyYW0ge09iamVjdH0gcHJvcHMgQmFnIG9mIGN1cnJlbnQgcHJvcGVydHkgY2hhbmdlcwogICAqIEByZXR1cm4ge2Jvb2xlYW59IFJldHVybnMgdHJ1ZSBpZiB0aGUgcGF0aCB3YXMgbm90aWZpZWQKICAgKiBAcHJpdmF0ZQogICAqLwogIGZ1bmN0aW9uIG5vdGlmeVBhdGgoaW5zdCwgcGF0aCwgcHJvcHMpIHsKICAgIGxldCByb290UHJvcGVydHkgPSBQb2x5bWVyLlBhdGgucm9vdChwYXRoKTsKICAgIGlmIChyb290UHJvcGVydHkgIT09IHBhdGgpIHsKICAgICAgbGV0IGV2ZW50TmFtZSA9IFBvbHltZXIuQ2FzZU1hcC5jYW1lbFRvRGFzaENhc2Uocm9vdFByb3BlcnR5KSArICctY2hhbmdlZCc7CiAgICAgIGRpc3BhdGNoTm90aWZ5RXZlbnQoaW5zdCwgZXZlbnROYW1lLCBwcm9wc1twYXRoXSwgcGF0aCk7CiAgICAgIHJldHVybiB0cnVlOwogICAgfQogICAgcmV0dXJuIGZhbHNlOwogIH0KCiAgLyoqCiAgICogRGlzcGF0Y2hlcyB7cHJvcGVydHl9LWNoYW5nZWQgZXZlbnRzIHRvIGluZGljYXRlIGEgcHJvcGVydHkgKG9yIHBhdGgpCiAgICogY2hhbmdlZC4KICAgKgogICAqIEBwYXJhbSB7IVByb3BlcnR5RWZmZWN0c1R5cGV9IGluc3QgVGhlIGVsZW1lbnQgZnJvbSB3aGljaCB0byBmaXJlIHRoZSBldmVudAogICAqIEBwYXJhbSB7c3RyaW5nfSBldmVudE5hbWUgVGhlIG5hbWUgb2YgdGhlIGV2ZW50IHRvIHNlbmQgKCd7cHJvcGVydHl9LWNoYW5nZWQnKQogICAqIEBwYXJhbSB7Kn0gdmFsdWUgVGhlIHZhbHVlIG9mIHRoZSBjaGFuZ2VkIHByb3BlcnR5CiAgICogQHBhcmFtIHtzdHJpbmcgfCBudWxsIHwgdW5kZWZpbmVkfSBwYXRoIElmIGEgc3ViLXBhdGggb2YgdGhpcyBwcm9wZXJ0eSBjaGFuZ2VkLCB0aGUgcGF0aAogICAqICAgdGhhdCBjaGFuZ2VkIChvcHRpb25hbCkuCiAgICogQHJldHVybiB7dm9pZH0KICAgKiBAcHJpdmF0ZQogICAqIEBzdXBwcmVzcyB7aW52YWxpZENhc3RzfQogICAqLwogIGZ1bmN0aW9uIGRpc3BhdGNoTm90aWZ5RXZlbnQoaW5zdCwgZXZlbnROYW1lLCB2YWx1ZSwgcGF0aCkgewogICAgbGV0IGRldGFpbCA9IHsKICAgICAgdmFsdWU6IHZhbHVlLAogICAgICBxdWV1ZVByb3BlcnR5OiB0cnVlCiAgICB9OwogICAgaWYgKHBhdGgpIHsKICAgICAgZGV0YWlsLnBhdGggPSBwYXRoOwogICAgfQogICAgLyoqIEB0eXBlIHshSFRNTEVsZW1lbnR9ICovKGluc3QpLmRpc3BhdGNoRXZlbnQobmV3IEN1c3RvbUV2ZW50KGV2ZW50TmFtZSwgeyBkZXRhaWwgfSkpOwogIH0KCiAgLyoqCiAgICogSW1wbGVtZW50cyB0aGUgIm5vdGlmeSIgZWZmZWN0LgogICAqCiAgICogRGlzcGF0Y2hlcyBhIG5vbi1idWJibGluZyBldmVudCBuYW1lZCBgaW5mby5ldmVudE5hbWVgIG9uIHRoZSBpbnN0YW5jZQogICAqIHdpdGggYSBkZXRhaWwgb2JqZWN0IGNvbnRhaW5pbmcgdGhlIG5ldyBgdmFsdWVgLgogICAqCiAgICogQHBhcmFtIHshUHJvcGVydHlFZmZlY3RzVHlwZX0gaW5zdCBUaGUgaW5zdGFuY2UgdGhlIGVmZmVjdCB3aWxsIGJlIHJ1biBvbgogICAqIEBwYXJhbSB7c3RyaW5nfSBwcm9wZXJ0eSBOYW1lIG9mIHByb3BlcnR5CiAgICogQHBhcmFtIHtPYmplY3R9IHByb3BzIEJhZyBvZiBjdXJyZW50IHByb3BlcnR5IGNoYW5nZXMKICAgKiBAcGFyYW0ge09iamVjdH0gb2xkUHJvcHMgQmFnIG9mIHByZXZpb3VzIHZhbHVlcyBmb3IgY2hhbmdlZCBwcm9wZXJ0aWVzCiAgICogQHBhcmFtIHs/fSBpbmZvIEVmZmVjdCBtZXRhZGF0YQogICAqIEBwYXJhbSB7Ym9vbGVhbn0gaGFzUGF0aHMgVHJ1ZSB3aXRoIGBwcm9wc2AgY29udGFpbnMgb25lIG9yIG1vcmUgcGF0aHMKICAgKiBAcmV0dXJuIHt2b2lkfQogICAqIEBwcml2YXRlCiAgICovCiAgZnVuY3Rpb24gcnVuTm90aWZ5RWZmZWN0KGluc3QsIHByb3BlcnR5LCBwcm9wcywgb2xkUHJvcHMsIGluZm8sIGhhc1BhdGhzKSB7CiAgICBsZXQgcm9vdFByb3BlcnR5ID0gaGFzUGF0aHMgPyBQb2x5bWVyLlBhdGgucm9vdChwcm9wZXJ0eSkgOiBwcm9wZXJ0eTsKICAgIGxldCBwYXRoID0gcm9vdFByb3BlcnR5ICE9IHByb3BlcnR5ID8gcHJvcGVydHkgOiBudWxsOwogICAgbGV0IHZhbHVlID0gcGF0aCA/IFBvbHltZXIuUGF0aC5nZXQoaW5zdCwgcGF0aCkgOiBpbnN0Ll9fZGF0YVtwcm9wZXJ0eV07CiAgICBpZiAocGF0aCAmJiB2YWx1ZSA9PT0gdW5kZWZpbmVkKSB7CiAgICAgIHZhbHVlID0gcHJvcHNbcHJvcGVydHldOyAgLy8gc3BlY2lmaWNhbGx5IGZvciAuc3BsaWNlcwogICAgfQogICAgZGlzcGF0Y2hOb3RpZnlFdmVudChpbnN0LCBpbmZvLmV2ZW50TmFtZSwgdmFsdWUsIHBhdGgpOwogIH0KCiAgLyoqCiAgICogSGFuZGxlciBmdW5jdGlvbiBmb3IgMi13YXkgbm90aWZpY2F0aW9uIGV2ZW50cy4gUmVjZWl2ZXMgY29udGV4dAogICAqIGluZm9ybWF0aW9uIGNhcHR1cmVkIGluIHRoZSBgYWRkTm90aWZ5TGlzdGVuZXJgIGNsb3N1cmUgZnJvbSB0aGUKICAgKiBgX19ub3RpZnlMaXN0ZW5lcnNgIG1ldGFkYXRhLgogICAqCiAgICogU2V0cyB0aGUgdmFsdWUgb2YgdGhlIG5vdGlmaWVkIHByb3BlcnR5IHRvIHRoZSBob3N0IHByb3BlcnR5IG9yIHBhdGguICBJZgogICAqIHRoZSBldmVudCBjb250YWluZWQgcGF0aCBpbmZvcm1hdGlvbiwgdHJhbnNsYXRlIHRoYXQgcGF0aCB0byB0aGUgaG9zdAogICAqIHNjb3BlJ3MgbmFtZSBmb3IgdGhhdCBwYXRoIGZpcnN0LgogICAqCiAgICogQHBhcmFtIHtDdXN0b21FdmVudH0gZXZlbnQgTm90aWZpY2F0aW9uIGV2ZW50IChlLmcuICc8cHJvcGVydHk+LWNoYW5nZWQnKQogICAqIEBwYXJhbSB7IVByb3BlcnR5RWZmZWN0c1R5cGV9IGluc3QgSG9zdCBlbGVtZW50IGluc3RhbmNlIGhhbmRsaW5nIHRoZSBub3RpZmljYXRpb24gZXZlbnQKICAgKiBAcGFyYW0ge3N0cmluZ30gZnJvbVByb3AgQ2hpbGQgZWxlbWVudCBwcm9wZXJ0eSB0aGF0IHdhcyBib3VuZAogICAqIEBwYXJhbSB7c3RyaW5nfSB0b1BhdGggSG9zdCBwcm9wZXJ0eS9wYXRoIHRoYXQgd2FzIGJvdW5kCiAgICogQHBhcmFtIHtib29sZWFufSBuZWdhdGUgV2hldGhlciB0aGUgYmluZGluZyB3YXMgbmVnYXRlZAogICAqIEByZXR1cm4ge3ZvaWR9CiAgICogQHByaXZhdGUKICAgKi8KICBmdW5jdGlvbiBoYW5kbGVOb3RpZmljYXRpb24oZXZlbnQsIGluc3QsIGZyb21Qcm9wLCB0b1BhdGgsIG5lZ2F0ZSkgewogICAgbGV0IHZhbHVlOwogICAgbGV0IGRldGFpbCA9IC8qKiBAdHlwZSB7T2JqZWN0fSAqLyhldmVudC5kZXRhaWwpOwogICAgbGV0IGZyb21QYXRoID0gZGV0YWlsICYmIGRldGFpbC5wYXRoOwogICAgaWYgKGZyb21QYXRoKSB7CiAgICAgIHRvUGF0aCA9IFBvbHltZXIuUGF0aC50cmFuc2xhdGUoZnJvbVByb3AsIHRvUGF0aCwgZnJvbVBhdGgpOwogICAgICB2YWx1ZSA9IGRldGFpbCAmJiBkZXRhaWwudmFsdWU7CiAgICB9IGVsc2UgewogICAgICB2YWx1ZSA9IGV2ZW50LmN1cnJlbnRUYXJnZXRbZnJvbVByb3BdOwogICAgfQogICAgdmFsdWUgPSBuZWdhdGUgPyAhdmFsdWUgOiB2YWx1ZTsKICAgIGlmICghaW5zdFtUWVBFUy5SRUFEX09OTFldIHx8ICFpbnN0W1RZUEVTLlJFQURfT05MWV1bdG9QYXRoXSkgewogICAgICBpZiAoaW5zdC5fc2V0UGVuZGluZ1Byb3BlcnR5T3JQYXRoKHRvUGF0aCwgdmFsdWUsIHRydWUsIEJvb2xlYW4oZnJvbVBhdGgpKQogICAgICAgICYmICghZGV0YWlsIHx8ICFkZXRhaWwucXVldWVQcm9wZXJ0eSkpIHsKICAgICAgICBpbnN0Ll9pbnZhbGlkYXRlUHJvcGVydGllcygpOwogICAgICB9CiAgICB9CiAgfQoKICAvKioKICAgKiBJbXBsZW1lbnRzIHRoZSAicmVmbGVjdCIgZWZmZWN0LgogICAqCiAgICogU2V0cyB0aGUgYXR0cmlidXRlIG5hbWVkIGBpbmZvLmF0dHJOYW1lYCB0byB0aGUgZ2l2ZW4gcHJvcGVydHkgdmFsdWUuCiAgICoKICAgKiBAcGFyYW0geyFQcm9wZXJ0eUVmZmVjdHNUeXBlfSBpbnN0IFRoZSBpbnN0YW5jZSB0aGUgZWZmZWN0IHdpbGwgYmUgcnVuIG9uCiAgICogQHBhcmFtIHtzdHJpbmd9IHByb3BlcnR5IE5hbWUgb2YgcHJvcGVydHkKICAgKiBAcGFyYW0ge09iamVjdH0gcHJvcHMgQmFnIG9mIGN1cnJlbnQgcHJvcGVydHkgY2hhbmdlcwogICAqIEBwYXJhbSB7T2JqZWN0fSBvbGRQcm9wcyBCYWcgb2YgcHJldmlvdXMgdmFsdWVzIGZvciBjaGFuZ2VkIHByb3BlcnRpZXMKICAgKiBAcGFyYW0gez99IGluZm8gRWZmZWN0IG1ldGFkYXRhCiAgICogQHJldHVybiB7dm9pZH0KICAgKiBAcHJpdmF0ZQogICAqLwogIGZ1bmN0aW9uIHJ1blJlZmxlY3RFZmZlY3QoaW5zdCwgcHJvcGVydHksIHByb3BzLCBvbGRQcm9wcywgaW5mbykgewogICAgbGV0IHZhbHVlID0gaW5zdC5fX2RhdGFbcHJvcGVydHldOwogICAgaWYgKFBvbHltZXIuc2FuaXRpemVET01WYWx1ZSkgewogICAgICB2YWx1ZSA9IFBvbHltZXIuc2FuaXRpemVET01WYWx1ZSh2YWx1ZSwgaW5mby5hdHRyTmFtZSwgJ2F0dHJpYnV0ZScsIC8qKiBAdHlwZSB7Tm9kZX0gKi8oaW5zdCkpOwogICAgfQogICAgaW5zdC5fcHJvcGVydHlUb0F0dHJpYnV0ZShwcm9wZXJ0eSwgaW5mby5hdHRyTmFtZSwgdmFsdWUpOwogIH0KCiAgLyoqCiAgICogUnVucyAiY29tcHV0ZWQiIGVmZmVjdHMgZm9yIGEgc2V0IG9mIGNoYW5nZWQgcHJvcGVydGllcy4KICAgKgogICAqIFRoaXMgbWV0aG9kIGRpZmZlcnMgZnJvbSB0aGUgZ2VuZXJpYyBgcnVuRWZmZWN0c2AgbWV0aG9kIGluIHRoYXQgaXQKICAgKiBjb250aW51ZXMgdG8gcnVuIGNvbXB1dGVkIGVmZmVjdHMgYmFzZWQgb24gdGhlIG91dHB1dCBvZiBlYWNoIHBhc3MgdW50aWwKICAgKiB0aGVyZSBhcmUgbm8gbW9yZSBuZXdseSBjb21wdXRlZCBwcm9wZXJ0aWVzLiAgVGhpcyBlbnN1cmVzIHRoYXQgYWxsCiAgICogcHJvcGVydGllcyB0aGF0IHdpbGwgYmUgY29tcHV0ZWQgYnkgdGhlIGluaXRpYWwgc2V0IG9mIGNoYW5nZXMgYXJlCiAgICogY29tcHV0ZWQgYmVmb3JlIG90aGVyIGVmZmVjdHMgKGJpbmRpbmcgcHJvcGFnYXRpb24sIG9ic2VydmVycywgYW5kIG5vdGlmeSkKICAgKiBydW4uCiAgICoKICAgKiBAcGFyYW0geyFQcm9wZXJ0eUVmZmVjdHNUeXBlfSBpbnN0IFRoZSBpbnN0YW5jZSB0aGUgZWZmZWN0IHdpbGwgYmUgcnVuIG9uCiAgICogQHBhcmFtIHshT2JqZWN0fSBjaGFuZ2VkUHJvcHMgQmFnIG9mIGNoYW5nZWQgcHJvcGVydGllcwogICAqIEBwYXJhbSB7IU9iamVjdH0gb2xkUHJvcHMgQmFnIG9mIHByZXZpb3VzIHZhbHVlcyBmb3IgY2hhbmdlZCBwcm9wZXJ0aWVzCiAgICogQHBhcmFtIHtib29sZWFufSBoYXNQYXRocyBUcnVlIHdpdGggYHByb3BzYCBjb250YWlucyBvbmUgb3IgbW9yZSBwYXRocwogICAqIEByZXR1cm4ge3ZvaWR9CiAgICogQHByaXZhdGUKICAgKi8KICBmdW5jdGlvbiBydW5Db21wdXRlZEVmZmVjdHMoaW5zdCwgY2hhbmdlZFByb3BzLCBvbGRQcm9wcywgaGFzUGF0aHMpIHsKICAgIGxldCBjb21wdXRlRWZmZWN0cyA9IGluc3RbVFlQRVMuQ09NUFVURV07CiAgICBpZiAoY29tcHV0ZUVmZmVjdHMpIHsKICAgICAgbGV0IGlucHV0UHJvcHMgPSBjaGFuZ2VkUHJvcHM7CiAgICAgIHdoaWxlIChydW5FZmZlY3RzKGluc3QsIGNvbXB1dGVFZmZlY3RzLCBpbnB1dFByb3BzLCBvbGRQcm9wcywgaGFzUGF0aHMpKSB7CiAgICAgICAgT2JqZWN0LmFzc2lnbihvbGRQcm9wcywgaW5zdC5fX2RhdGFPbGQpOwogICAgICAgIE9iamVjdC5hc3NpZ24oY2hhbmdlZFByb3BzLCBpbnN0Ll9fZGF0YVBlbmRpbmcpOwogICAgICAgIGlucHV0UHJvcHMgPSBpbnN0Ll9fZGF0YVBlbmRpbmc7CiAgICAgICAgaW5zdC5fX2RhdGFQZW5kaW5nID0gbnVsbDsKICAgICAgfQogICAgfQogIH0KCiAgLyoqCiAgICogSW1wbGVtZW50cyB0aGUgImNvbXB1dGVkIHByb3BlcnR5IiBlZmZlY3QgYnkgcnVubmluZyB0aGUgbWV0aG9kIHdpdGggdGhlCiAgICogdmFsdWVzIG9mIHRoZSBhcmd1bWVudHMgc3BlY2lmaWVkIGluIHRoZSBgaW5mb2Agb2JqZWN0IGFuZCBzZXR0aW5nIHRoZQogICAqIHJldHVybiB2YWx1ZSB0byB0aGUgY29tcHV0ZWQgcHJvcGVydHkgc3BlY2lmaWVkLgogICAqCiAgICogQHBhcmFtIHshUHJvcGVydHlFZmZlY3RzVHlwZX0gaW5zdCBUaGUgaW5zdGFuY2UgdGhlIGVmZmVjdCB3aWxsIGJlIHJ1biBvbgogICAqIEBwYXJhbSB7c3RyaW5nfSBwcm9wZXJ0eSBOYW1lIG9mIHByb3BlcnR5CiAgICogQHBhcmFtIHtPYmplY3R9IHByb3BzIEJhZyBvZiBjdXJyZW50IHByb3BlcnR5IGNoYW5nZXMKICAgKiBAcGFyYW0ge09iamVjdH0gb2xkUHJvcHMgQmFnIG9mIHByZXZpb3VzIHZhbHVlcyBmb3IgY2hhbmdlZCBwcm9wZXJ0aWVzCiAgICogQHBhcmFtIHs/fSBpbmZvIEVmZmVjdCBtZXRhZGF0YQogICAqIEByZXR1cm4ge3ZvaWR9CiAgICogQHByaXZhdGUKICAgKi8KICBmdW5jdGlvbiBydW5Db21wdXRlZEVmZmVjdChpbnN0LCBwcm9wZXJ0eSwgcHJvcHMsIG9sZFByb3BzLCBpbmZvKSB7CiAgICBsZXQgcmVzdWx0ID0gcnVuTWV0aG9kRWZmZWN0KGluc3QsIHByb3BlcnR5LCBwcm9wcywgb2xkUHJvcHMsIGluZm8pOwogICAgbGV0IGNvbXB1dGVkUHJvcCA9IGluZm8ubWV0aG9kSW5mbzsKICAgIGlmIChpbnN0Ll9fZGF0YUhhc0FjY2Vzc29yICYmIGluc3QuX19kYXRhSGFzQWNjZXNzb3JbY29tcHV0ZWRQcm9wXSkgewogICAgICBpbnN0Ll9zZXRQZW5kaW5nUHJvcGVydHkoY29tcHV0ZWRQcm9wLCByZXN1bHQsIHRydWUpOwogICAgfSBlbHNlIHsKICAgICAgaW5zdFtjb21wdXRlZFByb3BdID0gcmVzdWx0OwogICAgfQogIH0KCiAgLyoqCiAgICogQ29tcHV0ZXMgcGF0aCBjaGFuZ2VzIGJhc2VkIG9uIHBhdGggbGlua3Mgc2V0IHVwIHVzaW5nIHRoZSBgbGlua1BhdGhzYAogICAqIEFQSS4KICAgKgogICAqIEBwYXJhbSB7IVByb3BlcnR5RWZmZWN0c1R5cGV9IGluc3QgVGhlIGluc3RhbmNlIHdob3NlIHByb3BzIGFyZSBjaGFuZ2luZwogICAqIEBwYXJhbSB7c3RyaW5nIHwgIUFycmF5PChzdHJpbmd8bnVtYmVyKT59IHBhdGggUGF0aCB0aGF0IGhhcyBjaGFuZ2VkCiAgICogQHBhcmFtIHsqfSB2YWx1ZSBWYWx1ZSBvZiBjaGFuZ2VkIHBhdGgKICAgKiBAcmV0dXJuIHt2b2lkfQogICAqIEBwcml2YXRlCiAgICovCiAgZnVuY3Rpb24gY29tcHV0ZUxpbmtlZFBhdGhzKGluc3QsIHBhdGgsIHZhbHVlKSB7CiAgICBsZXQgbGlua3MgPSBpbnN0Ll9fZGF0YUxpbmtlZFBhdGhzOwogICAgaWYgKGxpbmtzKSB7CiAgICAgIGxldCBsaW5rOwogICAgICBmb3IgKGxldCBhIGluIGxpbmtzKSB7CiAgICAgICAgbGV0IGIgPSBsaW5rc1thXTsKICAgICAgICBpZiAoUG9seW1lci5QYXRoLmlzRGVzY2VuZGFudChhLCBwYXRoKSkgewogICAgICAgICAgbGluayA9IFBvbHltZXIuUGF0aC50cmFuc2xhdGUoYSwgYiwgcGF0aCk7CiAgICAgICAgICBpbnN0Ll9zZXRQZW5kaW5nUHJvcGVydHlPclBhdGgobGluaywgdmFsdWUsIHRydWUsIHRydWUpOwogICAgICAgIH0gZWxzZSBpZiAoUG9seW1lci5QYXRoLmlzRGVzY2VuZGFudChiLCBwYXRoKSkgewogICAgICAgICAgbGluayA9IFBvbHltZXIuUGF0aC50cmFuc2xhdGUoYiwgYSwgcGF0aCk7CiAgICAgICAgICBpbnN0Ll9zZXRQZW5kaW5nUHJvcGVydHlPclBhdGgobGluaywgdmFsdWUsIHRydWUsIHRydWUpOwogICAgICAgIH0KICAgICAgfQogICAgfQogIH0KCiAgLy8gLS0gYmluZGluZ3MgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKICAvKioKICAgKiBBZGRzIGJpbmRpbmcgbWV0YWRhdGEgdG8gdGhlIGN1cnJlbnQgYG5vZGVJbmZvYCwgYW5kIGJpbmRpbmcgZWZmZWN0cwogICAqIGZvciBhbGwgcGFydCBkZXBlbmRlbmNpZXMgdG8gYHRlbXBsYXRlSW5mb2AuCiAgICoKICAgKiBAcGFyYW0ge0Z1bmN0aW9ufSBjb25zdHJ1Y3RvciBDbGFzcyB0aGF0IGBfcGFyc2VUZW1wbGF0ZWAgaXMgY3VycmVudGx5CiAgICogICBydW5uaW5nIG9uCiAgICogQHBhcmFtIHtUZW1wbGF0ZUluZm99IHRlbXBsYXRlSW5mbyBUZW1wbGF0ZSBtZXRhZGF0YSBmb3IgY3VycmVudCB0ZW1wbGF0ZQogICAqIEBwYXJhbSB7Tm9kZUluZm99IG5vZGVJbmZvIE5vZGUgbWV0YWRhdGEgZm9yIGN1cnJlbnQgdGVtcGxhdGUgbm9kZQogICAqIEBwYXJhbSB7c3RyaW5nfSBraW5kIEJpbmRpbmcga2luZCwgZWl0aGVyICdwcm9wZXJ0eScsICdhdHRyaWJ1dGUnLCBvciAndGV4dCcKICAgKiBAcGFyYW0ge3N0cmluZ30gdGFyZ2V0IFRhcmdldCBwcm9wZXJ0eSBuYW1lCiAgICogQHBhcmFtIHshQXJyYXk8IUJpbmRpbmdQYXJ0Pn0gcGFydHMgQXJyYXkgb2YgYmluZGluZyBwYXJ0IG1ldGFkYXRhCiAgICogQHBhcmFtIHtzdHJpbmc9fSBsaXRlcmFsIExpdGVyYWwgdGV4dCBzdXJyb3VuZGluZyBiaW5kaW5nIHBhcnRzIChzcGVjaWZpZWQKICAgKiAgIG9ubHkgZm9yICdwcm9wZXJ0eScgYmluZGluZ3MsIHNpbmNlIHRoZXNlIG11c3QgYmUgaW5pdGlhbGl6ZWQgYXMgcGFydAogICAqICAgb2YgYm9vdC11cCkKICAgKiBAcmV0dXJuIHt2b2lkfQogICAqIEBwcml2YXRlCiAgICovCiAgZnVuY3Rpb24gYWRkQmluZGluZyhjb25zdHJ1Y3RvciwgdGVtcGxhdGVJbmZvLCBub2RlSW5mbywga2luZCwgdGFyZ2V0LCBwYXJ0cywgbGl0ZXJhbCkgewogICAgLy8gQ3JlYXRlIGJpbmRpbmcgbWV0YWRhdGEgYW5kIGFkZCB0byBub2RlSW5mbwogICAgbm9kZUluZm8uYmluZGluZ3MgPSBub2RlSW5mby5iaW5kaW5ncyB8fCBbXTsKICAgIGxldCAvKiogQmluZGluZyAqLyBiaW5kaW5nID0geyBraW5kLCB0YXJnZXQsIHBhcnRzLCBsaXRlcmFsLCBpc0NvbXBvdW5kOiAocGFydHMubGVuZ3RoICE9PSAxKSB9OwogICAgbm9kZUluZm8uYmluZGluZ3MucHVzaChiaW5kaW5nKTsKICAgIC8vIEFkZCBsaXN0ZW5lciBpbmZvIHRvIGJpbmRpbmcgbWV0YWRhdGEKICAgIGlmIChzaG91bGRBZGRMaXN0ZW5lcihiaW5kaW5nKSkgewogICAgICBsZXQge2V2ZW50LCBuZWdhdGV9ID0gYmluZGluZy5wYXJ0c1swXTsKICAgICAgYmluZGluZy5saXN0ZW5lckV2ZW50ID0gZXZlbnQgfHwgKENhc2VNYXAuY2FtZWxUb0Rhc2hDYXNlKHRhcmdldCkgKyAnLWNoYW5nZWQnKTsKICAgICAgYmluZGluZy5saXN0ZW5lck5lZ2F0ZSA9IG5lZ2F0ZTsKICAgIH0KICAgIC8vIEFkZCAicHJvcGFnYXRlIiBwcm9wZXJ0eSBlZmZlY3RzIHRvIHRlbXBsYXRlSW5mbwogICAgbGV0IGluZGV4ID0gdGVtcGxhdGVJbmZvLm5vZGVJbmZvTGlzdC5sZW5ndGg7CiAgICBmb3IgKGxldCBpPTA7IGk8YmluZGluZy5wYXJ0cy5sZW5ndGg7IGkrKykgewogICAgICBsZXQgcGFydCA9IGJpbmRpbmcucGFydHNbaV07CiAgICAgIHBhcnQuY29tcG91bmRJbmRleCA9IGk7CiAgICAgIGFkZEVmZmVjdEZvckJpbmRpbmdQYXJ0KGNvbnN0cnVjdG9yLCB0ZW1wbGF0ZUluZm8sIGJpbmRpbmcsIHBhcnQsIGluZGV4KTsKICAgIH0KICB9CgogIC8qKgogICAqIEFkZHMgcHJvcGVydHkgZWZmZWN0cyB0byB0aGUgZ2l2ZW4gYHRlbXBsYXRlSW5mb2AgZm9yIHRoZSBnaXZlbiBiaW5kaW5nCiAgICogcGFydC4KICAgKgogICAqIEBwYXJhbSB7RnVuY3Rpb259IGNvbnN0cnVjdG9yIENsYXNzIHRoYXQgYF9wYXJzZVRlbXBsYXRlYCBpcyBjdXJyZW50bHkKICAgKiAgIHJ1bm5pbmcgb24KICAgKiBAcGFyYW0ge1RlbXBsYXRlSW5mb30gdGVtcGxhdGVJbmZvIFRlbXBsYXRlIG1ldGFkYXRhIGZvciBjdXJyZW50IHRlbXBsYXRlCiAgICogQHBhcmFtIHshQmluZGluZ30gYmluZGluZyBCaW5kaW5nIG1ldGFkYXRhCiAgICogQHBhcmFtIHshQmluZGluZ1BhcnR9IHBhcnQgQmluZGluZyBwYXJ0IG1ldGFkYXRhCiAgICogQHBhcmFtIHtudW1iZXJ9IGluZGV4IEluZGV4IGludG8gYG5vZGVJbmZvTGlzdGAgZm9yIHRoaXMgbm9kZQogICAqIEByZXR1cm4ge3ZvaWR9CiAgICovCiAgZnVuY3Rpb24gYWRkRWZmZWN0Rm9yQmluZGluZ1BhcnQoY29uc3RydWN0b3IsIHRlbXBsYXRlSW5mbywgYmluZGluZywgcGFydCwgaW5kZXgpIHsKICAgIGlmICghcGFydC5saXRlcmFsKSB7CiAgICAgIGlmIChiaW5kaW5nLmtpbmQgPT09ICdhdHRyaWJ1dGUnICYmIGJpbmRpbmcudGFyZ2V0WzBdID09PSAnLScpIHsKICAgICAgICBjb25zb2xlLndhcm4oJ0Nhbm5vdCBzZXQgYXR0cmlidXRlICcgKyBiaW5kaW5nLnRhcmdldCArCiAgICAgICAgICAnIGJlY2F1c2UgIi0iIGlzIG5vdCBhIHZhbGlkIGF0dHJpYnV0ZSBzdGFydGluZyBjaGFyYWN0ZXInKTsKICAgICAgfSBlbHNlIHsKICAgICAgICBsZXQgZGVwZW5kZW5jaWVzID0gcGFydC5kZXBlbmRlbmNpZXM7CiAgICAgICAgbGV0IGluZm8gPSB7IGluZGV4LCBiaW5kaW5nLCBwYXJ0LCBldmFsdWF0b3I6IGNvbnN0cnVjdG9yIH07CiAgICAgICAgZm9yIChsZXQgaj0wOyBqPGRlcGVuZGVuY2llcy5sZW5ndGg7IGorKykgewogICAgICAgICAgbGV0IHRyaWdnZXIgPSBkZXBlbmRlbmNpZXNbal07CiAgICAgICAgICBpZiAodHlwZW9mIHRyaWdnZXIgPT0gJ3N0cmluZycpIHsKICAgICAgICAgICAgdHJpZ2dlciA9IHBhcnNlQXJnKHRyaWdnZXIpOwogICAgICAgICAgICB0cmlnZ2VyLndpbGRjYXJkID0gdHJ1ZTsKICAgICAgICAgIH0KICAgICAgICAgIGNvbnN0cnVjdG9yLl9hZGRUZW1wbGF0ZVByb3BlcnR5RWZmZWN0KHRlbXBsYXRlSW5mbywgdHJpZ2dlci5yb290UHJvcGVydHksIHsKICAgICAgICAgICAgZm46IHJ1bkJpbmRpbmdFZmZlY3QsCiAgICAgICAgICAgIGluZm8sIHRyaWdnZXIKICAgICAgICAgIH0pOwogICAgICAgIH0KICAgICAgfQogICAgfQogIH0KCiAgLyoqCiAgICogSW1wbGVtZW50cyB0aGUgImJpbmRpbmciIChwcm9wZXJ0eS9wYXRoIGJpbmRpbmcpIGVmZmVjdC4KICAgKgogICAqIE5vdGUgdGhhdCBiaW5kaW5nIHN5bnRheCBpcyBvdmVycmlkYWJsZSB2aWEgYF9wYXJzZUJpbmRpbmdzYCBhbmQKICAgKiBgX2V2YWx1YXRlQmluZGluZ2AuICBUaGlzIG1ldGhvZCB3aWxsIGNhbGwgYF9ldmFsdWF0ZUJpbmRpbmdgIGZvciBhbnkKICAgKiBub24tbGl0ZXJhbCBwYXJ0cyByZXR1cm5lZCBmcm9tIGBfcGFyc2VCaW5kaW5nc2AuICBIb3dldmVyLAogICAqIHRoZXJlIGlzIG5vIHN1cHBvcnQgZm9yIF9wYXRoXyBiaW5kaW5ncyB2aWEgY3VzdG9tIGJpbmRpbmcgcGFydHMsCiAgICogYXMgdGhpcyBpcyBzcGVjaWZpYyB0byBQb2x5bWVyJ3MgcGF0aCBiaW5kaW5nIHN5bnRheC4KICAgKgogICAqIEBwYXJhbSB7IVByb3BlcnR5RWZmZWN0c1R5cGV9IGluc3QgVGhlIGluc3RhbmNlIHRoZSBlZmZlY3Qgd2lsbCBiZSBydW4gb24KICAgKiBAcGFyYW0ge3N0cmluZ30gcGF0aCBOYW1lIG9mIHByb3BlcnR5CiAgICogQHBhcmFtIHtPYmplY3R9IHByb3BzIEJhZyBvZiBjdXJyZW50IHByb3BlcnR5IGNoYW5nZXMKICAgKiBAcGFyYW0ge09iamVjdH0gb2xkUHJvcHMgQmFnIG9mIHByZXZpb3VzIHZhbHVlcyBmb3IgY2hhbmdlZCBwcm9wZXJ0aWVzCiAgICogQHBhcmFtIHs/fSBpbmZvIEVmZmVjdCBtZXRhZGF0YQogICAqIEBwYXJhbSB7Ym9vbGVhbn0gaGFzUGF0aHMgVHJ1ZSB3aXRoIGBwcm9wc2AgY29udGFpbnMgb25lIG9yIG1vcmUgcGF0aHMKICAgKiBAcGFyYW0ge0FycmF5fSBub2RlTGlzdCBMaXN0IG9mIG5vZGVzIGFzc29jaWF0ZWQgd2l0aCBgbm9kZUluZm9MaXN0YCB0ZW1wbGF0ZQogICAqICAgbWV0YWRhdGEKICAgKiBAcmV0dXJuIHt2b2lkfQogICAqIEBwcml2YXRlCiAgICovCiAgZnVuY3Rpb24gcnVuQmluZGluZ0VmZmVjdChpbnN0LCBwYXRoLCBwcm9wcywgb2xkUHJvcHMsIGluZm8sIGhhc1BhdGhzLCBub2RlTGlzdCkgewogICAgbGV0IG5vZGUgPSBub2RlTGlzdFtpbmZvLmluZGV4XTsKICAgIGxldCBiaW5kaW5nID0gaW5mby5iaW5kaW5nOwogICAgbGV0IHBhcnQgPSBpbmZvLnBhcnQ7CiAgICAvLyBTdWJwYXRoIG5vdGlmaWNhdGlvbjogdHJhbnNmb3JtIHBhdGggYW5kIHNldCB0byBjbGllbnQKICAgIC8vIGUuZy46IGZvbz0ie3tvYmouc3VifX0iLCBwYXRoOiAnb2JqLnN1Yi5wcm9wJywgc2V0ICdmb28ucHJvcCc9b2JqLnN1Yi5wcm9wCiAgICBpZiAoaGFzUGF0aHMgJiYgcGFydC5zb3VyY2UgJiYgKHBhdGgubGVuZ3RoID4gcGFydC5zb3VyY2UubGVuZ3RoKSAmJgogICAgICAgIChiaW5kaW5nLmtpbmQgPT0gJ3Byb3BlcnR5JykgJiYgIWJpbmRpbmcuaXNDb21wb3VuZCAmJgogICAgICAgIG5vZGUuX19pc1Byb3BlcnR5RWZmZWN0c0NsaWVudCAmJgogICAgICAgIG5vZGUuX19kYXRhSGFzQWNjZXNzb3IgJiYgbm9kZS5fX2RhdGFIYXNBY2Nlc3NvcltiaW5kaW5nLnRhcmdldF0pIHsKICAgICAgbGV0IHZhbHVlID0gcHJvcHNbcGF0aF07CiAgICAgIHBhdGggPSBQb2x5bWVyLlBhdGgudHJhbnNsYXRlKHBhcnQuc291cmNlLCBiaW5kaW5nLnRhcmdldCwgcGF0aCk7CiAgICAgIGlmIChub2RlLl9zZXRQZW5kaW5nUHJvcGVydHlPclBhdGgocGF0aCwgdmFsdWUsIGZhbHNlLCB0cnVlKSkgewogICAgICAgIGluc3QuX2VucXVldWVDbGllbnQobm9kZSk7CiAgICAgIH0KICAgIH0gZWxzZSB7CiAgICAgIGxldCB2YWx1ZSA9IGluZm8uZXZhbHVhdG9yLl9ldmFsdWF0ZUJpbmRpbmcoaW5zdCwgcGFydCwgcGF0aCwgcHJvcHMsIG9sZFByb3BzLCBoYXNQYXRocyk7CiAgICAgIC8vIFByb3BhZ2F0ZSB2YWx1ZSB0byBjaGlsZAogICAgICBhcHBseUJpbmRpbmdWYWx1ZShpbnN0LCBub2RlLCBiaW5kaW5nLCBwYXJ0LCB2YWx1ZSk7CiAgICB9CiAgfQoKICAvKioKICAgKiBTZXRzIHRoZSB2YWx1ZSBmb3IgYW4gImJpbmRpbmciIChiaW5kaW5nKSBlZmZlY3QgdG8gYSBub2RlLAogICAqIGVpdGhlciBhcyBhIHByb3BlcnR5IG9yIGF0dHJpYnV0ZS4KICAgKgogICAqIEBwYXJhbSB7IVByb3BlcnR5RWZmZWN0c1R5cGV9IGluc3QgVGhlIGluc3RhbmNlIG93bmluZyB0aGUgYmluZGluZyBlZmZlY3QKICAgKiBAcGFyYW0ge05vZGV9IG5vZGUgVGFyZ2V0IG5vZGUgZm9yIGJpbmRpbmcKICAgKiBAcGFyYW0geyFCaW5kaW5nfSBiaW5kaW5nIEJpbmRpbmcgbWV0YWRhdGEKICAgKiBAcGFyYW0geyFCaW5kaW5nUGFydH0gcGFydCBCaW5kaW5nIHBhcnQgbWV0YWRhdGEKICAgKiBAcGFyYW0geyp9IHZhbHVlIFZhbHVlIHRvIHNldAogICAqIEByZXR1cm4ge3ZvaWR9CiAgICogQHByaXZhdGUKICAgKi8KICBmdW5jdGlvbiBhcHBseUJpbmRpbmdWYWx1ZShpbnN0LCBub2RlLCBiaW5kaW5nLCBwYXJ0LCB2YWx1ZSkgewogICAgdmFsdWUgPSBjb21wdXRlQmluZGluZ1ZhbHVlKG5vZGUsIHZhbHVlLCBiaW5kaW5nLCBwYXJ0KTsKICAgIGlmIChQb2x5bWVyLnNhbml0aXplRE9NVmFsdWUpIHsKICAgICAgdmFsdWUgPSBQb2x5bWVyLnNhbml0aXplRE9NVmFsdWUodmFsdWUsIGJpbmRpbmcudGFyZ2V0LCBiaW5kaW5nLmtpbmQsIG5vZGUpOwogICAgfQogICAgaWYgKGJpbmRpbmcua2luZCA9PSAnYXR0cmlidXRlJykgewogICAgICAvLyBBdHRyaWJ1dGUgYmluZGluZwogICAgICBpbnN0Ll92YWx1ZVRvTm9kZUF0dHJpYnV0ZSgvKiogQHR5cGUge0VsZW1lbnR9ICovKG5vZGUpLCB2YWx1ZSwgYmluZGluZy50YXJnZXQpOwogICAgfSBlbHNlIHsKICAgICAgLy8gUHJvcGVydHkgYmluZGluZwogICAgICBsZXQgcHJvcCA9IGJpbmRpbmcudGFyZ2V0OwogICAgICBpZiAobm9kZS5fX2lzUHJvcGVydHlFZmZlY3RzQ2xpZW50ICYmCiAgICAgICAgICBub2RlLl9fZGF0YUhhc0FjY2Vzc29yICYmIG5vZGUuX19kYXRhSGFzQWNjZXNzb3JbcHJvcF0pIHsKICAgICAgICBpZiAoIW5vZGVbVFlQRVMuUkVBRF9PTkxZXSB8fCAhbm9kZVtUWVBFUy5SRUFEX09OTFldW3Byb3BdKSB7CiAgICAgICAgICBpZiAobm9kZS5fc2V0UGVuZGluZ1Byb3BlcnR5KHByb3AsIHZhbHVlKSkgewogICAgICAgICAgICBpbnN0Ll9lbnF1ZXVlQ2xpZW50KG5vZGUpOwogICAgICAgICAgfQogICAgICAgIH0KICAgICAgfSBlbHNlICB7CiAgICAgICAgaW5zdC5fc2V0VW5tYW5hZ2VkUHJvcGVydHlUb05vZGUobm9kZSwgcHJvcCwgdmFsdWUpOwogICAgICB9CiAgICB9CiAgfQoKICAvKioKICAgKiBUcmFuc2Zvcm1zIGFuICJiaW5kaW5nIiBlZmZlY3QgdmFsdWUgYmFzZWQgb24gY29tcG91bmQgJiBuZWdhdGlvbgogICAqIGVmZmVjdCBtZXRhZGF0YSwgYXMgd2VsbCBhcyBoYW5kbGluZyBmb3Igc3BlY2lhbC1jYXNlIHByb3BlcnRpZXMKICAgKgogICAqIEBwYXJhbSB7Tm9kZX0gbm9kZSBOb2RlIHRoZSB2YWx1ZSB3aWxsIGJlIHNldCB0bwogICAqIEBwYXJhbSB7Kn0gdmFsdWUgVmFsdWUgdG8gc2V0CiAgICogQHBhcmFtIHshQmluZGluZ30gYmluZGluZyBCaW5kaW5nIG1ldGFkYXRhCiAgICogQHBhcmFtIHshQmluZGluZ1BhcnR9IHBhcnQgQmluZGluZyBwYXJ0IG1ldGFkYXRhCiAgICogQHJldHVybiB7Kn0gVHJhbnNmb3JtZWQgdmFsdWUgdG8gc2V0CiAgICogQHByaXZhdGUKICAgKi8KICBmdW5jdGlvbiBjb21wdXRlQmluZGluZ1ZhbHVlKG5vZGUsIHZhbHVlLCBiaW5kaW5nLCBwYXJ0KSB7CiAgICBpZiAoYmluZGluZy5pc0NvbXBvdW5kKSB7CiAgICAgIGxldCBzdG9yYWdlID0gbm9kZS5fX2RhdGFDb21wb3VuZFN0b3JhZ2VbYmluZGluZy50YXJnZXRdOwogICAgICBzdG9yYWdlW3BhcnQuY29tcG91bmRJbmRleF0gPSB2YWx1ZTsKICAgICAgdmFsdWUgPSBzdG9yYWdlLmpvaW4oJycpOwogICAgfQogICAgaWYgKGJpbmRpbmcua2luZCAhPT0gJ2F0dHJpYnV0ZScpIHsKICAgICAgLy8gU29tZSBicm93c2VycyBzZXJpYWxpemUgYHVuZGVmaW5lZGAgdG8gYCJ1bmRlZmluZWQiYAogICAgICBpZiAoYmluZGluZy50YXJnZXQgPT09ICd0ZXh0Q29udGVudCcgfHwKICAgICAgICAgIChiaW5kaW5nLnRhcmdldCA9PT0gJ3ZhbHVlJyAmJgogICAgICAgICAgICAobm9kZS5sb2NhbE5hbWUgPT09ICdpbnB1dCcgfHwgbm9kZS5sb2NhbE5hbWUgPT09ICd0ZXh0YXJlYScpKSkgewogICAgICAgIHZhbHVlID0gdmFsdWUgPT0gdW5kZWZpbmVkID8gJycgOiB2YWx1ZTsKICAgICAgfQogICAgfQogICAgcmV0dXJuIHZhbHVlOwogIH0KCiAgLyoqCiAgICogUmV0dXJucyB0cnVlIGlmIGEgYmluZGluZydzIG1ldGFkYXRhIG1lZXRzIGFsbCB0aGUgcmVxdWlyZW1lbnRzIHRvIGFsbG93CiAgICogMi13YXkgYmluZGluZywgYW5kIHRoZXJlZm9yZSBhIGA8cHJvcGVydHk+LWNoYW5nZWRgIGV2ZW50IGxpc3RlbmVyIHNob3VsZCBiZQogICAqIGFkZGVkOgogICAqIC0gdXNlZCBjdXJseSBicmFjZXMKICAgKiAtIGlzIGEgcHJvcGVydHkgKG5vdCBhdHRyaWJ1dGUpIGJpbmRpbmcKICAgKiAtIGlzIG5vdCBhIHRleHRDb250ZW50IGJpbmRpbmcKICAgKiAtIGlzIG5vdCBjb21wb3VuZAogICAqCiAgICogQHBhcmFtIHshQmluZGluZ30gYmluZGluZyBCaW5kaW5nIG1ldGFkYXRhCiAgICogQHJldHVybiB7Ym9vbGVhbn0gVHJ1ZSBpZiAyLXdheSBsaXN0ZW5lciBzaG91bGQgYmUgYWRkZWQKICAgKiBAcHJpdmF0ZQogICAqLwogIGZ1bmN0aW9uIHNob3VsZEFkZExpc3RlbmVyKGJpbmRpbmcpIHsKICAgIHJldHVybiBCb29sZWFuKGJpbmRpbmcudGFyZ2V0KSAmJgogICAgICAgICAgIGJpbmRpbmcua2luZCAhPSAnYXR0cmlidXRlJyAmJgogICAgICAgICAgIGJpbmRpbmcua2luZCAhPSAndGV4dCcgJiYKICAgICAgICAgICAhYmluZGluZy5pc0NvbXBvdW5kICYmCiAgICAgICAgICAgYmluZGluZy5wYXJ0c1swXS5tb2RlID09PSAneyc7CiAgfQoKICAvKioKICAgKiBTZXR1cCBjb21wb3VuZCBiaW5kaW5nIHN0b3JhZ2Ugc3RydWN0dXJlcywgbm90aWZ5IGxpc3RlbmVycywgYW5kIGRhdGFIb3N0CiAgICogcmVmZXJlbmNlcyBvbnRvIHRoZSBib3VuZCBub2RlTGlzdC4KICAgKgogICAqIEBwYXJhbSB7IVByb3BlcnR5RWZmZWN0c1R5cGV9IGluc3QgSW5zdGFuY2UgdGhhdCBiYXMgYmVlbiBwcmV2aW91c2x5IGJvdW5kCiAgICogQHBhcmFtIHtUZW1wbGF0ZUluZm99IHRlbXBsYXRlSW5mbyBUZW1wbGF0ZSBtZXRhZGF0YQogICAqIEByZXR1cm4ge3ZvaWR9CiAgICogQHByaXZhdGUKICAgKi8KICBmdW5jdGlvbiBzZXR1cEJpbmRpbmdzKGluc3QsIHRlbXBsYXRlSW5mbykgewogICAgLy8gU2V0dXAgY29tcG91bmQgc3RvcmFnZSwgZGF0YUhvc3QsIGFuZCBub3RpZnkgbGlzdGVuZXJzCiAgICBsZXQge25vZGVMaXN0LCBub2RlSW5mb0xpc3R9ID0gdGVtcGxhdGVJbmZvOwogICAgaWYgKG5vZGVJbmZvTGlzdC5sZW5ndGgpIHsKICAgICAgZm9yIChsZXQgaT0wOyBpIDwgbm9kZUluZm9MaXN0Lmxlbmd0aDsgaSsrKSB7CiAgICAgICAgbGV0IGluZm8gPSBub2RlSW5mb0xpc3RbaV07CiAgICAgICAgbGV0IG5vZGUgPSBub2RlTGlzdFtpXTsKICAgICAgICBsZXQgYmluZGluZ3MgPSBpbmZvLmJpbmRpbmdzOwogICAgICAgIGlmIChiaW5kaW5ncykgewogICAgICAgICAgZm9yIChsZXQgaT0wOyBpPGJpbmRpbmdzLmxlbmd0aDsgaSsrKSB7CiAgICAgICAgICAgIGxldCBiaW5kaW5nID0gYmluZGluZ3NbaV07CiAgICAgICAgICAgIHNldHVwQ29tcG91bmRTdG9yYWdlKG5vZGUsIGJpbmRpbmcpOwogICAgICAgICAgICBhZGROb3RpZnlMaXN0ZW5lcihub2RlLCBpbnN0LCBiaW5kaW5nKTsKICAgICAgICAgIH0KICAgICAgICB9CiAgICAgICAgbm9kZS5fX2RhdGFIb3N0ID0gaW5zdDsKICAgICAgfQogICAgfQogIH0KCiAgLyoqCiAgICogSW5pdGlhbGl6ZXMgYF9fZGF0YUNvbXBvdW5kU3RvcmFnZWAgbG9jYWwgc3RvcmFnZSBvbiBhIGJvdW5kIG5vZGUgd2l0aAogICAqIGluaXRpYWwgbGl0ZXJhbCBkYXRhIGZvciBjb21wb3VuZCBiaW5kaW5ncywgYW5kIHNldHMgdGhlIGpvaW5lZAogICAqIGxpdGVyYWwgcGFydHMgdG8gdGhlIGJvdW5kIHByb3BlcnR5LgogICAqCiAgICogV2hlbiBjaGFuZ2VzIHRvIGNvbXBvdW5kIHBhcnRzIG9jY3VyLCB0aGV5IGFyZSBmaXJzdCBzZXQgaW50byB0aGUgY29tcG91bmQKICAgKiBzdG9yYWdlIGFycmF5IGZvciB0aGF0IHByb3BlcnR5LCBhbmQgdGhlbiB0aGUgYXJyYXkgaXMgam9pbmVkIHRvIHJlc3VsdCBpbgogICAqIHRoZSBmaW5hbCB2YWx1ZSBzZXQgdG8gdGhlIHByb3BlcnR5L2F0dHJpYnV0ZS4KICAgKgogICAqIEBwYXJhbSB7Tm9kZX0gbm9kZSBCb3VuZCBub2RlIHRvIGluaXRpYWxpemUKICAgKiBAcGFyYW0ge0JpbmRpbmd9IGJpbmRpbmcgQmluZGluZyBtZXRhZGF0YQogICAqIEByZXR1cm4ge3ZvaWR9CiAgICogQHByaXZhdGUKICAgKi8KICBmdW5jdGlvbiBzZXR1cENvbXBvdW5kU3RvcmFnZShub2RlLCBiaW5kaW5nKSB7CiAgICBpZiAoYmluZGluZy5pc0NvbXBvdW5kKSB7CiAgICAgIC8vIENyZWF0ZSBjb21wb3VuZCBzdG9yYWdlIG1hcAogICAgICBsZXQgc3RvcmFnZSA9IG5vZGUuX19kYXRhQ29tcG91bmRTdG9yYWdlIHx8CiAgICAgICAgKG5vZGUuX19kYXRhQ29tcG91bmRTdG9yYWdlID0ge30pOwogICAgICBsZXQgcGFydHMgPSBiaW5kaW5nLnBhcnRzOwogICAgICAvLyBDb3B5IGxpdGVyYWxzIGZyb20gcGFydHMgaW50byBzdG9yYWdlIGZvciB0aGlzIGJpbmRpbmcKICAgICAgbGV0IGxpdGVyYWxzID0gbmV3IEFycmF5KHBhcnRzLmxlbmd0aCk7CiAgICAgIGZvciAobGV0IGo9MDsgajxwYXJ0cy5sZW5ndGg7IGorKykgewogICAgICAgIGxpdGVyYWxzW2pdID0gcGFydHNbal0ubGl0ZXJhbDsKICAgICAgfQogICAgICBsZXQgdGFyZ2V0ID0gYmluZGluZy50YXJnZXQ7CiAgICAgIHN0b3JhZ2VbdGFyZ2V0XSA9IGxpdGVyYWxzOwogICAgICAvLyBDb25maWd1cmUgcHJvcGVydGllcyB3aXRoIHRoZWlyIGxpdGVyYWwgcGFydHMKICAgICAgaWYgKGJpbmRpbmcubGl0ZXJhbCAmJiBiaW5kaW5nLmtpbmQgPT0gJ3Byb3BlcnR5JykgewogICAgICAgIG5vZGVbdGFyZ2V0XSA9IGJpbmRpbmcubGl0ZXJhbDsKICAgICAgfQogICAgfQogIH0KCiAgLyoqCiAgICogQWRkcyBhIDItd2F5IGJpbmRpbmcgbm90aWZpY2F0aW9uIGV2ZW50IGxpc3RlbmVyIHRvIHRoZSBub2RlIHNwZWNpZmllZAogICAqCiAgICogQHBhcmFtIHtPYmplY3R9IG5vZGUgQ2hpbGQgZWxlbWVudCB0byBhZGQgbGlzdGVuZXIgdG8KICAgKiBAcGFyYW0geyFQcm9wZXJ0eUVmZmVjdHNUeXBlfSBpbnN0IEhvc3QgZWxlbWVudCBpbnN0YW5jZSB0byBoYW5kbGUgbm90aWZpY2F0aW9uIGV2ZW50CiAgICogQHBhcmFtIHtCaW5kaW5nfSBiaW5kaW5nIEJpbmRpbmcgbWV0YWRhdGEKICAgKiBAcmV0dXJuIHt2b2lkfQogICAqIEBwcml2YXRlCiAgICovCiAgZnVuY3Rpb24gYWRkTm90aWZ5TGlzdGVuZXIobm9kZSwgaW5zdCwgYmluZGluZykgewogICAgaWYgKGJpbmRpbmcubGlzdGVuZXJFdmVudCkgewogICAgICBsZXQgcGFydCA9IGJpbmRpbmcucGFydHNbMF07CiAgICAgIG5vZGUuYWRkRXZlbnRMaXN0ZW5lcihiaW5kaW5nLmxpc3RlbmVyRXZlbnQsIGZ1bmN0aW9uKGUpIHsKICAgICAgICBoYW5kbGVOb3RpZmljYXRpb24oZSwgaW5zdCwgYmluZGluZy50YXJnZXQsIHBhcnQuc291cmNlLCBwYXJ0Lm5lZ2F0ZSk7CiAgICAgIH0pOwogICAgfQogIH0KCiAgLy8gLS0gZm9yIG1ldGhvZC1iYXNlZCBlZmZlY3RzIChjb21wbGV4T2JzZXJ2ZXIgJiBjb21wdXRlZCkgLS0tLS0tLS0tLS0tLS0KCiAgLyoqCiAgICogQWRkcyBwcm9wZXJ0eSBlZmZlY3RzIGZvciBlYWNoIGFyZ3VtZW50IGluIHRoZSBtZXRob2Qgc2lnbmF0dXJlIChhbmQKICAgKiBvcHRpb25hbGx5LCBmb3IgdGhlIG1ldGhvZCBuYW1lIGlmIGBkeW5hbWljYCBpcyB0cnVlKSB0aGF0IGNhbGxzIHRoZQogICAqIHByb3ZpZGVkIGVmZmVjdCBmdW5jdGlvbi4KICAgKgogICAqIEBwYXJhbSB7RWxlbWVudCB8IE9iamVjdH0gbW9kZWwgUHJvdG90eXBlIG9yIGluc3RhbmNlCiAgICogQHBhcmFtIHshTWV0aG9kU2lnbmF0dXJlfSBzaWcgTWV0aG9kIHNpZ25hdHVyZSBtZXRhZGF0YQogICAqIEBwYXJhbSB7c3RyaW5nfSB0eXBlIFR5cGUgb2YgcHJvcGVydHkgZWZmZWN0IHRvIGFkZAogICAqIEBwYXJhbSB7RnVuY3Rpb259IGVmZmVjdEZuIEZ1bmN0aW9uIHRvIHJ1biB3aGVuIGFyZ3VtZW50cyBjaGFuZ2UKICAgKiBAcGFyYW0geyo9fSBtZXRob2RJbmZvIEVmZmVjdC1zcGVjaWZpYyBpbmZvcm1hdGlvbiB0byBiZSBpbmNsdWRlZCBpbgogICAqICAgbWV0aG9kIGVmZmVjdCBtZXRhZGF0YQogICAqIEBwYXJhbSB7Ym9vbGVhbnxPYmplY3Q9fSBkeW5hbWljRm4gQm9vbGVhbiBvciBvYmplY3QgbWFwIGluZGljYXRpbmcgd2hldGhlcgogICAqICAgbWV0aG9kIG5hbWVzIHNob3VsZCBiZSBpbmNsdWRlZCBhcyBhIGRlcGVuZGVuY3kgdG8gdGhlIGVmZmVjdC4gTm90ZSwKICAgKiAgIGRlZmF1bHRzIHRvIHRydWUgaWYgdGhlIHNpZ25hdHVyZSBpcyBzdGF0aWMgKHNpZy5zdGF0aWMgaXMgdHJ1ZSkuCiAgICogQHJldHVybiB7dm9pZH0KICAgKiBAcHJpdmF0ZQogICAqLwogIGZ1bmN0aW9uIGNyZWF0ZU1ldGhvZEVmZmVjdChtb2RlbCwgc2lnLCB0eXBlLCBlZmZlY3RGbiwgbWV0aG9kSW5mbywgZHluYW1pY0ZuKSB7CiAgICBkeW5hbWljRm4gPSBzaWcuc3RhdGljIHx8IChkeW5hbWljRm4gJiYKICAgICAgKHR5cGVvZiBkeW5hbWljRm4gIT09ICdvYmplY3QnIHx8IGR5bmFtaWNGbltzaWcubWV0aG9kTmFtZV0pKTsKICAgIGxldCBpbmZvID0gewogICAgICBtZXRob2ROYW1lOiBzaWcubWV0aG9kTmFtZSwKICAgICAgYXJnczogc2lnLmFyZ3MsCiAgICAgIG1ldGhvZEluZm8sCiAgICAgIGR5bmFtaWNGbgogICAgfTsKICAgIGZvciAobGV0IGk9MCwgYXJnOyAoaTxzaWcuYXJncy5sZW5ndGgpICYmIChhcmc9c2lnLmFyZ3NbaV0pOyBpKyspIHsKICAgICAgaWYgKCFhcmcubGl0ZXJhbCkgewogICAgICAgIG1vZGVsLl9hZGRQcm9wZXJ0eUVmZmVjdChhcmcucm9vdFByb3BlcnR5LCB0eXBlLCB7CiAgICAgICAgICBmbjogZWZmZWN0Rm4sIGluZm86IGluZm8sIHRyaWdnZXI6IGFyZwogICAgICAgIH0pOwogICAgICB9CiAgICB9CiAgICBpZiAoZHluYW1pY0ZuKSB7CiAgICAgIG1vZGVsLl9hZGRQcm9wZXJ0eUVmZmVjdChzaWcubWV0aG9kTmFtZSwgdHlwZSwgewogICAgICAgIGZuOiBlZmZlY3RGbiwgaW5mbzogaW5mbwogICAgICB9KTsKICAgIH0KICB9CgogIC8qKgogICAqIENhbGxzIGEgbWV0aG9kIHdpdGggYXJndW1lbnRzIG1hcnNoYWxlZCBmcm9tIHByb3BlcnRpZXMgb24gdGhlIGluc3RhbmNlCiAgICogYmFzZWQgb24gdGhlIG1ldGhvZCBzaWduYXR1cmUgY29udGFpbmVkIGluIHRoZSBlZmZlY3QgbWV0YWRhdGEuCiAgICoKICAgKiBNdWx0aS1wcm9wZXJ0eSBvYnNlcnZlcnMsIGNvbXB1dGVkIHByb3BlcnRpZXMsIGFuZCBpbmxpbmUgY29tcHV0aW5nCiAgICogZnVuY3Rpb25zIGNhbGwgdGhpcyBmdW5jdGlvbiB0byBpbnZva2UgdGhlIG1ldGhvZCwgdGhlbiB1c2UgdGhlIHJldHVybgogICAqIHZhbHVlIGFjY29yZGluZ2x5LgogICAqCiAgICogQHBhcmFtIHshUHJvcGVydHlFZmZlY3RzVHlwZX0gaW5zdCBUaGUgaW5zdGFuY2UgdGhlIGVmZmVjdCB3aWxsIGJlIHJ1biBvbgogICAqIEBwYXJhbSB7c3RyaW5nfSBwcm9wZXJ0eSBOYW1lIG9mIHByb3BlcnR5CiAgICogQHBhcmFtIHtPYmplY3R9IHByb3BzIEJhZyBvZiBjdXJyZW50IHByb3BlcnR5IGNoYW5nZXMKICAgKiBAcGFyYW0ge09iamVjdH0gb2xkUHJvcHMgQmFnIG9mIHByZXZpb3VzIHZhbHVlcyBmb3IgY2hhbmdlZCBwcm9wZXJ0aWVzCiAgICogQHBhcmFtIHs/fSBpbmZvIEVmZmVjdCBtZXRhZGF0YQogICAqIEByZXR1cm4geyp9IFJldHVybnMgdGhlIHJldHVybiB2YWx1ZSBmcm9tIHRoZSBtZXRob2QgaW52b2NhdGlvbgogICAqIEBwcml2YXRlCiAgICovCiAgZnVuY3Rpb24gcnVuTWV0aG9kRWZmZWN0KGluc3QsIHByb3BlcnR5LCBwcm9wcywgb2xkUHJvcHMsIGluZm8pIHsKICAgIC8vIEluc3RhbmNlcyBjYW4gb3B0aW9uYWxseSBoYXZlIGEgX21ldGhvZEhvc3Qgd2hpY2ggYWxsb3dzIHJlZGlyZWN0aW5nIHdoZXJlCiAgICAvLyB0byBmaW5kIG1ldGhvZHMuIEN1cnJlbnRseSB1c2VkIGJ5IGB0ZW1wbGF0aXplYC4KICAgIGxldCBjb250ZXh0ID0gaW5zdC5fbWV0aG9kSG9zdCB8fCBpbnN0OwogICAgbGV0IGZuID0gY29udGV4dFtpbmZvLm1ldGhvZE5hbWVdOwogICAgaWYgKGZuKSB7CiAgICAgIGxldCBhcmdzID0gaW5zdC5fbWFyc2hhbEFyZ3MoaW5mby5hcmdzLCBwcm9wZXJ0eSwgcHJvcHMpOwogICAgICByZXR1cm4gZm4uYXBwbHkoY29udGV4dCwgYXJncyk7CiAgICB9IGVsc2UgaWYgKCFpbmZvLmR5bmFtaWNGbikgewogICAgICBjb25zb2xlLndhcm4oJ21ldGhvZCBgJyArIGluZm8ubWV0aG9kTmFtZSArICdgIG5vdCBkZWZpbmVkJyk7CiAgICB9CiAgfQoKICBjb25zdCBlbXB0eUFycmF5ID0gW107CgogIC8vIFJlZ3VsYXIgZXhwcmVzc2lvbnMgdXNlZCBmb3IgYmluZGluZwogIGNvbnN0IElERU5UICA9ICcoPzonICsgJ1thLXpBLVpfJF1bXFx3LjokXFwtKl0qJyArICcpJzsKICBjb25zdCBOVU1CRVIgPSAnKD86JyArICdbLStdP1swLTldKlxcLj9bMC05XSsoPzpbZUVdWy0rXT9bMC05XSspPycgKyAnKSc7CiAgY29uc3QgU1FVT1RFX1NUUklORyA9ICcoPzonICsgJ1wnKD86W15cJ1xcXFxdfFxcXFwuKSpcJycgKyAnKSc7CiAgY29uc3QgRFFVT1RFX1NUUklORyA9ICcoPzonICsgJyIoPzpbXiJcXFxcXXxcXFxcLikqIicgKyAnKSc7CiAgY29uc3QgU1RSSU5HID0gJyg/OicgKyBTUVVPVEVfU1RSSU5HICsgJ3wnICsgRFFVT1RFX1NUUklORyArICcpJzsKICBjb25zdCBBUkdVTUVOVCA9ICcoPzooJyArIElERU5UICsgJ3wnICsgTlVNQkVSICsgJ3wnICsgIFNUUklORyArICcpXFxzKicgKyAnKSc7CiAgY29uc3QgQVJHVU1FTlRTID0gJyg/OicgKyBBUkdVTUVOVCArICcoPzosXFxzKicgKyBBUkdVTUVOVCArICcpKicgKyAnKSc7CiAgY29uc3QgQVJHVU1FTlRfTElTVCA9ICcoPzonICsgJ1xcKFxccyonICsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnKD86JyArIEFSR1VNRU5UUyArICc/JyArICcpJyArCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdcXClcXHMqJyArICcpJzsKICBjb25zdCBCSU5ESU5HID0gJygnICsgSURFTlQgKyAnXFxzKicgKyBBUkdVTUVOVF9MSVNUICsgJz8nICsgJyknOyAvLyBHcm91cCAzCiAgY29uc3QgT1BFTl9CUkFDS0VUID0gJyhcXFtcXFt8e3spJyArICdcXHMqJzsKICBjb25zdCBDTE9TRV9CUkFDS0VUID0gJyg/Ol1dfH19KSc7CiAgY29uc3QgTkVHQVRFID0gJyg/OighKVxccyopPyc7IC8vIEdyb3VwIDIKICBjb25zdCBFWFBSRVNTSU9OID0gT1BFTl9CUkFDS0VUICsgTkVHQVRFICsgQklORElORyArIENMT1NFX0JSQUNLRVQ7CiAgY29uc3QgYmluZGluZ1JlZ2V4ID0gbmV3IFJlZ0V4cChFWFBSRVNTSU9OLCAiZyIpOwoKICAvKioKICAgKiBDcmVhdGUgYSBzdHJpbmcgZnJvbSBiaW5kaW5nIHBhcnRzIG9mIGFsbCB0aGUgbGl0ZXJhbCBwYXJ0cwogICAqCiAgICogQHBhcmFtIHshQXJyYXk8QmluZGluZ1BhcnQ+fSBwYXJ0cyBBbGwgcGFydHMgdG8gc3RyaW5naWZ5CiAgICogQHJldHVybiB7c3RyaW5nfSBTdHJpbmcgbWFkZSBmcm9tIHRoZSBsaXRlcmFsIHBhcnRzCiAgICovCiAgZnVuY3Rpb24gbGl0ZXJhbEZyb21QYXJ0cyhwYXJ0cykgewogICAgbGV0IHMgPSAnJzsKICAgIGZvciAobGV0IGk9MDsgaTxwYXJ0cy5sZW5ndGg7IGkrKykgewogICAgICBsZXQgbGl0ZXJhbCA9IHBhcnRzW2ldLmxpdGVyYWw7CiAgICAgIHMgKz0gbGl0ZXJhbCB8fCAnJzsKICAgIH0KICAgIHJldHVybiBzOwogIH0KCiAgLyoqCiAgICogUGFyc2VzIGFuIGV4cHJlc3Npb24gc3RyaW5nIGZvciBhIG1ldGhvZCBzaWduYXR1cmUsIGFuZCByZXR1cm5zIGEgbWV0YWRhdGEKICAgKiBkZXNjcmliaW5nIHRoZSBtZXRob2QgaW4gdGVybXMgb2YgYG1ldGhvZE5hbWVgLCBgc3RhdGljYCAod2hldGhlciBhbGwgdGhlCiAgICogYXJndW1lbnRzIGFyZSBsaXRlcmFscyksIGFuZCBhbiBhcnJheSBvZiBgYXJnc2AKICAgKgogICAqIEBwYXJhbSB7c3RyaW5nfSBleHByZXNzaW9uIFRoZSBleHByZXNzaW9uIHRvIHBhcnNlCiAgICogQHJldHVybiB7P01ldGhvZFNpZ25hdHVyZX0gVGhlIG1ldGhvZCBtZXRhZGF0YSBvYmplY3QgaWYgYSBtZXRob2QgZXhwcmVzc2lvbiB3YXMKICAgKiAgIGZvdW5kLCBvdGhlcndpc2UgYHVuZGVmaW5lZGAKICAgKiBAcHJpdmF0ZQogICAqLwogIGZ1bmN0aW9uIHBhcnNlTWV0aG9kKGV4cHJlc3Npb24pIHsKICAgIC8vIHRyaWVzIHRvIG1hdGNoIHZhbGlkIGphdmFzY3JpcHQgcHJvcGVydHkgbmFtZXMKICAgIGxldCBtID0gZXhwcmVzc2lvbi5tYXRjaCgvKFteXHNdKz8pXCgoW1xzXFNdKilcKS8pOwogICAgaWYgKG0pIHsKICAgICAgbGV0IG1ldGhvZE5hbWUgPSBtWzFdOwogICAgICBsZXQgc2lnID0geyBtZXRob2ROYW1lLCBzdGF0aWM6IHRydWUsIGFyZ3M6IGVtcHR5QXJyYXkgfTsKICAgICAgaWYgKG1bMl0udHJpbSgpKSB7CiAgICAgICAgLy8gcmVwbGFjZSBlc2NhcGVkIGNvbW1hcyB3aXRoIGNvbW1hIGVudGl0eSwgc3BsaXQgb24gdW4tZXNjYXBlZCBjb21tYXMKICAgICAgICBsZXQgYXJncyA9IG1bMl0ucmVwbGFjZSgvXFwsL2csICcmY29tbWE7Jykuc3BsaXQoJywnKTsKICAgICAgICByZXR1cm4gcGFyc2VBcmdzKGFyZ3MsIHNpZyk7CiAgICAgIH0gZWxzZSB7CiAgICAgICAgcmV0dXJuIHNpZzsKICAgICAgfQogICAgfQogICAgcmV0dXJuIG51bGw7CiAgfQoKICAvKioKICAgKiBQYXJzZXMgYW4gYXJyYXkgb2YgYXJndW1lbnRzIGFuZCBzZXRzIHRoZSBgYXJnc2AgcHJvcGVydHkgb2YgdGhlIHN1cHBsaWVkCiAgICogc2lnbmF0dXJlIG1ldGFkYXRhIG9iamVjdC4gU2V0cyB0aGUgYHN0YXRpY2AgcHJvcGVydHkgdG8gZmFsc2UgaWYgYW55CiAgICogYXJndW1lbnQgaXMgYSBub24tbGl0ZXJhbC4KICAgKgogICAqIEBwYXJhbSB7IUFycmF5PHN0cmluZz59IGFyZ0xpc3QgQXJyYXkgb2YgYXJndW1lbnQgbmFtZXMKICAgKiBAcGFyYW0geyFNZXRob2RTaWduYXR1cmV9IHNpZyBNZXRob2Qgc2lnbmF0dXJlIG1ldGFkYXRhIG9iamVjdAogICAqIEByZXR1cm4geyFNZXRob2RTaWduYXR1cmV9IFRoZSB1cGRhdGVkIHNpZ25hdHVyZSBtZXRhZGF0YSBvYmplY3QKICAgKiBAcHJpdmF0ZQogICAqLwogIGZ1bmN0aW9uIHBhcnNlQXJncyhhcmdMaXN0LCBzaWcpIHsKICAgIHNpZy5hcmdzID0gYXJnTGlzdC5tYXAoZnVuY3Rpb24ocmF3QXJnKSB7CiAgICAgIGxldCBhcmcgPSBwYXJzZUFyZyhyYXdBcmcpOwogICAgICBpZiAoIWFyZy5saXRlcmFsKSB7CiAgICAgICAgc2lnLnN0YXRpYyA9IGZhbHNlOwogICAgICB9CiAgICAgIHJldHVybiBhcmc7CiAgICB9LCB0aGlzKTsKICAgIHJldHVybiBzaWc7CiAgfQoKICAvKioKICAgKiBQYXJzZXMgYW4gaW5kaXZpZHVhbCBhcmd1bWVudCwgYW5kIHJldHVybnMgYW4gYXJndW1lbnQgbWV0YWRhdGEgb2JqZWN0CiAgICogd2l0aCB0aGUgZm9sbG93aW5nIGZpZWxkczoKICAgKgogICAqICAgewogICAqICAgICB2YWx1ZTogJ3Byb3AnLCAgICAgICAgLy8gcHJvcGVydHkvcGF0aCBvciBsaXRlcmFsIHZhbHVlCiAgICogICAgIGxpdGVyYWw6IGZhbHNlLCAgICAgICAvLyB3aGV0aGVyIGFyZ3VtZW50IGlzIGEgbGl0ZXJhbAogICAqICAgICBzdHJ1Y3R1cmVkOiBmYWxzZSwgICAgLy8gd2hldGhlciB0aGUgcHJvcGVydHkgaXMgYSBwYXRoCiAgICogICAgIHJvb3RQcm9wZXJ0eTogJ3Byb3AnLCAvLyB0aGUgcm9vdCBwcm9wZXJ0eSBvZiB0aGUgcGF0aAogICAqICAgICB3aWxkY2FyZDogZmFsc2UgICAgICAgLy8gd2hldGhlciB0aGUgYXJndW1lbnQgd2FzIGEgd2lsZGNhcmQgJy4qJyBwYXRoCiAgICogICB9CiAgICoKICAgKiBAcGFyYW0ge3N0cmluZ30gcmF3QXJnIFRoZSBzdHJpbmcgdmFsdWUgb2YgdGhlIGFyZ3VtZW50CiAgICogQHJldHVybiB7IU1ldGhvZEFyZ30gQXJndW1lbnQgbWV0YWRhdGEgb2JqZWN0CiAgICogQHByaXZhdGUKICAgKi8KICBmdW5jdGlvbiBwYXJzZUFyZyhyYXdBcmcpIHsKICAgIC8vIGNsZWFuIHVwIHdoaXRlc3BhY2UKICAgIGxldCBhcmcgPSByYXdBcmcudHJpbSgpCiAgICAgIC8vIHJlcGxhY2UgY29tbWEgZW50aXR5IHdpdGggY29tbWEKICAgICAgLnJlcGxhY2UoLyZjb21tYTsvZywgJywnKQogICAgICAvLyByZXBhaXIgZXh0cmEgZXNjYXBlIHNlcXVlbmNlczsgbm90ZSBvbmx5IGNvbW1hcyBzdHJpY3RseSBuZWVkCiAgICAgIC8vIGVzY2FwaW5nLCBidXQgd2UgYWxsb3cgYW55IG90aGVyIGNoYXIgdG8gYmUgZXNjYXBlZCBzaW5jZSBpdHMKICAgICAgLy8gbGlrZWx5IHVzZXJzIHdpbGwgZG8gdGhpcwogICAgICAucmVwbGFjZSgvXFwoLikvZywgJ1wkMScpCiAgICAgIDsKICAgIC8vIGJhc2ljIGFyZ3VtZW50IGRlc2NyaXB0b3IKICAgIGxldCBhID0gewogICAgICBuYW1lOiBhcmcsCiAgICAgIHZhbHVlOiAnJywKICAgICAgbGl0ZXJhbDogZmFsc2UKICAgIH07CiAgICAvLyBkZXRlY3QgbGl0ZXJhbCB2YWx1ZSAobXVzdCBiZSBTdHJpbmcgb3IgTnVtYmVyKQogICAgbGV0IGZjID0gYXJnWzBdOwogICAgaWYgKGZjID09PSAnLScpIHsKICAgICAgZmMgPSBhcmdbMV07CiAgICB9CiAgICBpZiAoZmMgPj0gJzAnICYmIGZjIDw9ICc5JykgewogICAgICBmYyA9ICcjJzsKICAgIH0KICAgIHN3aXRjaChmYykgewogICAgICBjYXNlICInIjoKICAgICAgY2FzZSAnIic6CiAgICAgICAgYS52YWx1ZSA9IGFyZy5zbGljZSgxLCAtMSk7CiAgICAgICAgYS5saXRlcmFsID0gdHJ1ZTsKICAgICAgICBicmVhazsKICAgICAgY2FzZSAnIyc6CiAgICAgICAgYS52YWx1ZSA9IE51bWJlcihhcmcpOwogICAgICAgIGEubGl0ZXJhbCA9IHRydWU7CiAgICAgICAgYnJlYWs7CiAgICB9CiAgICAvLyBpZiBub3QgbGl0ZXJhbCwgbG9vayBmb3Igc3RydWN0dXJlZCBwYXRoCiAgICBpZiAoIWEubGl0ZXJhbCkgewogICAgICBhLnJvb3RQcm9wZXJ0eSA9IFBvbHltZXIuUGF0aC5yb290KGFyZyk7CiAgICAgIC8vIGRldGVjdCBzdHJ1Y3R1cmVkIHBhdGggKGhhcyBkb3RzKQogICAgICBhLnN0cnVjdHVyZWQgPSBQb2x5bWVyLlBhdGguaXNQYXRoKGFyZyk7CiAgICAgIGlmIChhLnN0cnVjdHVyZWQpIHsKICAgICAgICBhLndpbGRjYXJkID0gKGFyZy5zbGljZSgtMikgPT0gJy4qJyk7CiAgICAgICAgaWYgKGEud2lsZGNhcmQpIHsKICAgICAgICAgIGEubmFtZSA9IGFyZy5zbGljZSgwLCAtMik7CiAgICAgICAgfQogICAgICB9CiAgICB9CiAgICByZXR1cm4gYTsKICB9CgogIC8vIGRhdGEgYXBpCgogIC8qKgogICAqIFNlbmRzIGFycmF5IHNwbGljZSBub3RpZmljYXRpb25zIChgLnNwbGljZXNgIGFuZCBgLmxlbmd0aGApCiAgICoKICAgKiBOb3RlOiB0aGlzIGltcGxlbWVudGF0aW9uIG9ubHkgYWNjZXB0cyBub3JtYWxpemVkIHBhdGhzCiAgICoKICAgKiBAcGFyYW0geyFQcm9wZXJ0eUVmZmVjdHNUeXBlfSBpbnN0IEluc3RhbmNlIHRvIHNlbmQgbm90aWZpY2F0aW9ucyB0bwogICAqIEBwYXJhbSB7QXJyYXl9IGFycmF5IFRoZSBhcnJheSB0aGUgbXV0YXRpb25zIG9jY3VycmVkIG9uCiAgICogQHBhcmFtIHtzdHJpbmd9IHBhdGggVGhlIHBhdGggdG8gdGhlIGFycmF5IHRoYXQgd2FzIG11dGF0ZWQKICAgKiBAcGFyYW0ge0FycmF5fSBzcGxpY2VzIEFycmF5IG9mIHNwbGljZSByZWNvcmRzCiAgICogQHJldHVybiB7dm9pZH0KICAgKiBAcHJpdmF0ZQogICAqLwogIGZ1bmN0aW9uIG5vdGlmeVNwbGljZXMoaW5zdCwgYXJyYXksIHBhdGgsIHNwbGljZXMpIHsKICAgIGxldCBzcGxpY2VzUGF0aCA9IHBhdGggKyAnLnNwbGljZXMnOwogICAgaW5zdC5ub3RpZnlQYXRoKHNwbGljZXNQYXRoLCB7IGluZGV4U3BsaWNlczogc3BsaWNlcyB9KTsKICAgIGluc3Qubm90aWZ5UGF0aChwYXRoICsgJy5sZW5ndGgnLCBhcnJheS5sZW5ndGgpOwogICAgLy8gTnVsbCBoZXJlIHRvIGFsbG93IHBvdGVudGlhbGx5IGxhcmdlIHNwbGljZSByZWNvcmRzIHRvIGJlIEdDJ2VkLgogICAgaW5zdC5fX2RhdGFbc3BsaWNlc1BhdGhdID0ge2luZGV4U3BsaWNlczogbnVsbH07CiAgfQoKICAvKioKICAgKiBDcmVhdGVzIGEgc3BsaWNlIHJlY29yZCBhbmQgc2VuZHMgYW4gYXJyYXkgc3BsaWNlIG5vdGlmaWNhdGlvbiBmb3IKICAgKiB0aGUgZGVzY3JpYmVkIG11dGF0aW9uCiAgICoKICAgKiBOb3RlOiB0aGlzIGltcGxlbWVudGF0aW9uIG9ubHkgYWNjZXB0cyBub3JtYWxpemVkIHBhdGhzCiAgICoKICAgKiBAcGFyYW0geyFQcm9wZXJ0eUVmZmVjdHNUeXBlfSBpbnN0IEluc3RhbmNlIHRvIHNlbmQgbm90aWZpY2F0aW9ucyB0bwogICAqIEBwYXJhbSB7QXJyYXl9IGFycmF5IFRoZSBhcnJheSB0aGUgbXV0YXRpb25zIG9jY3VycmVkIG9uCiAgICogQHBhcmFtIHtzdHJpbmd9IHBhdGggVGhlIHBhdGggdG8gdGhlIGFycmF5IHRoYXQgd2FzIG11dGF0ZWQKICAgKiBAcGFyYW0ge251bWJlcn0gaW5kZXggSW5kZXggYXQgd2hpY2ggdGhlIGFycmF5IG11dGF0aW9uIG9jY3VycmVkCiAgICogQHBhcmFtIHtudW1iZXJ9IGFkZGVkQ291bnQgTnVtYmVyIG9mIGFkZGVkIGl0ZW1zCiAgICogQHBhcmFtIHtBcnJheX0gcmVtb3ZlZCBBcnJheSBvZiByZW1vdmVkIGl0ZW1zCiAgICogQHJldHVybiB7dm9pZH0KICAgKiBAcHJpdmF0ZQogICAqLwogIGZ1bmN0aW9uIG5vdGlmeVNwbGljZShpbnN0LCBhcnJheSwgcGF0aCwgaW5kZXgsIGFkZGVkQ291bnQsIHJlbW92ZWQpIHsKICAgIG5vdGlmeVNwbGljZXMoaW5zdCwgYXJyYXksIHBhdGgsIFt7CiAgICAgIGluZGV4OiBpbmRleCwKICAgICAgYWRkZWRDb3VudDogYWRkZWRDb3VudCwKICAgICAgcmVtb3ZlZDogcmVtb3ZlZCwKICAgICAgb2JqZWN0OiBhcnJheSwKICAgICAgdHlwZTogJ3NwbGljZScKICAgIH1dKTsKICB9CgogIC8qKgogICAqIFJldHVybnMgYW4gdXBwZXItY2FzZWQgdmVyc2lvbiBvZiB0aGUgc3RyaW5nLgogICAqCiAgICogQHBhcmFtIHtzdHJpbmd9IG5hbWUgU3RyaW5nIHRvIHVwcGVyY2FzZQogICAqIEByZXR1cm4ge3N0cmluZ30gVXBwZXJjYXNlZCBzdHJpbmcKICAgKiBAcHJpdmF0ZQogICAqLwogIGZ1bmN0aW9uIHVwcGVyKG5hbWUpIHsKICAgIHJldHVybiBuYW1lWzBdLnRvVXBwZXJDYXNlKCkgKyBuYW1lLnN1YnN0cmluZygxKTsKICB9CgogIC8qKgogICAqIEVsZW1lbnQgY2xhc3MgbWl4aW4gdGhhdCBwcm92aWRlcyBtZXRhLXByb2dyYW1taW5nIGZvciBQb2x5bWVyJ3MgdGVtcGxhdGUKICAgKiBiaW5kaW5nIGFuZCBkYXRhIG9ic2VydmF0aW9uIChjb2xsZWN0aXZlbHksICJwcm9wZXJ0eSBlZmZlY3RzIikgc3lzdGVtLgogICAqCiAgICogVGhpcyBtaXhpbiB1c2VzIHByb3ZpZGVzIHRoZSBmb2xsb3dpbmcga2V5IHN0YXRpYyBtZXRob2RzIGZvciBhZGRpbmcKICAgKiBwcm9wZXJ0eSBlZmZlY3RzIHRvIGFuIGVsZW1lbnQgY2xhc3M6CiAgICogLSBgYWRkUHJvcGVydHlFZmZlY3RgCiAgICogLSBgY3JlYXRlUHJvcGVydHlPYnNlcnZlcmAKICAgKiAtIGBjcmVhdGVNZXRob2RPYnNlcnZlcmAKICAgKiAtIGBjcmVhdGVOb3RpZnlpbmdQcm9wZXJ0eWAKICAgKiAtIGBjcmVhdGVSZWFkT25seVByb3BlcnR5YAogICAqIC0gYGNyZWF0ZVJlZmxlY3RlZFByb3BlcnR5YAogICAqIC0gYGNyZWF0ZUNvbXB1dGVkUHJvcGVydHlgCiAgICogLSBgYmluZFRlbXBsYXRlYAogICAqCiAgICogRWFjaCBtZXRob2QgY3JlYXRlcyBvbmUgb3IgbW9yZSBwcm9wZXJ0eSBhY2Nlc3NvcnMsIGFsb25nIHdpdGggbWV0YWRhdGEKICAgKiB1c2VkIGJ5IHRoaXMgbWl4aW4ncyBpbXBsZW1lbnRhdGlvbiBvZiBgX3Byb3BlcnRpZXNDaGFuZ2VkYCB0byBwZXJmb3JtCiAgICogdGhlIHByb3BlcnR5IGVmZmVjdHMuCiAgICoKICAgKiBVbmRlcnNjb3JlZCB2ZXJzaW9ucyBvZiB0aGUgYWJvdmUgbWV0aG9kcyBhbHNvIGV4aXN0IG9uIHRoZSBlbGVtZW50CiAgICogcHJvdG90eXBlIGZvciBhZGRpbmcgcHJvcGVydHkgZWZmZWN0cyBvbiBpbnN0YW5jZXMgYXQgcnVudGltZS4KICAgKgogICAqIE5vdGUgdGhhdCB0aGlzIG1peGluIG92ZXJyaWRlcyBzZXZlcmFsIGBQcm9wZXJ0eUFjY2Vzc29yc2AgbWV0aG9kcywgaW4KICAgKiBtYW55IGNhc2VzIHRvIG1haW50YWluIGd1YXJhbnRlZXMgcHJvdmlkZWQgYnkgdGhlIFBvbHltZXIgMS54IGZlYXR1cmVzOwogICAqIG5vdGFibHkgaXQgY2hhbmdlcyBwcm9wZXJ0eSBhY2Nlc3NvcnMgdG8gYmUgc3luY2hyb25vdXMgYnkgZGVmYXVsdAogICAqIHdoZXJlYXMgdGhlIGRlZmF1bHQgd2hlbiB1c2luZyBgUHJvcGVydHlBY2Nlc3NvcnNgIHN0YW5kYWxvbmUgaXMgdG8gYmUKICAgKiBhc3luYyBieSBkZWZhdWx0LgogICAqCiAgICogQG1peGluRnVuY3Rpb24KICAgKiBAcG9seW1lcgogICAqIEBhcHBsaWVzTWl4aW4gUG9seW1lci5UZW1wbGF0ZVN0YW1wCiAgICogQGFwcGxpZXNNaXhpbiBQb2x5bWVyLlByb3BlcnR5QWNjZXNzb3JzCiAgICogQG1lbWJlcm9mIFBvbHltZXIKICAgKiBAc3VtbWFyeSBFbGVtZW50IGNsYXNzIG1peGluIHRoYXQgcHJvdmlkZXMgbWV0YS1wcm9ncmFtbWluZyBmb3IgUG9seW1lcidzCiAgICogdGVtcGxhdGUgYmluZGluZyBhbmQgZGF0YSBvYnNlcnZhdGlvbiBzeXN0ZW0uCiAgICovCiAgUG9seW1lci5Qcm9wZXJ0eUVmZmVjdHMgPSBQb2x5bWVyLmRlZHVwaW5nTWl4aW4oc3VwZXJDbGFzcyA9PiB7CgogICAgLyoqCiAgICAgKiBAY29uc3RydWN0b3IKICAgICAqIEBleHRlbmRzIHtzdXBlckNsYXNzfQogICAgICogQGltcGxlbWVudHMge1BvbHltZXJfUHJvcGVydHlBY2Nlc3NvcnN9CiAgICAgKiBAaW1wbGVtZW50cyB7UG9seW1lcl9UZW1wbGF0ZVN0YW1wfQogICAgICogQHVucmVzdHJpY3RlZAogICAgICogQHByaXZhdGUKICAgICAqLwogICAgY29uc3QgcHJvcGVydHlFZmZlY3RzQmFzZSA9IFBvbHltZXIuVGVtcGxhdGVTdGFtcChQb2x5bWVyLlByb3BlcnR5QWNjZXNzb3JzKHN1cGVyQ2xhc3MpKTsKCiAgICAvKioKICAgICAqIEBwb2x5bWVyCiAgICAgKiBAbWl4aW5DbGFzcwogICAgICogQGltcGxlbWVudHMge1BvbHltZXJfUHJvcGVydHlFZmZlY3RzfQogICAgICogQGV4dGVuZHMge3Byb3BlcnR5RWZmZWN0c0Jhc2V9CiAgICAgKiBAdW5yZXN0cmljdGVkCiAgICAgKi8KICAgIGNsYXNzIFByb3BlcnR5RWZmZWN0cyBleHRlbmRzIHByb3BlcnR5RWZmZWN0c0Jhc2UgewoKICAgICAgY29uc3RydWN0b3IoKSB7CiAgICAgICAgc3VwZXIoKTsKICAgICAgICAvKiogQHR5cGUge2Jvb2xlYW59ICovCiAgICAgICAgLy8gVXNlZCB0byBpZGVudGlmeSB1c2VycyBvZiB0aGlzIG1peGluLCBhbGEgaW5zdGFuY2VvZgogICAgICAgIHRoaXMuX19pc1Byb3BlcnR5RWZmZWN0c0NsaWVudCA9IHRydWU7CiAgICAgICAgLyoqIEB0eXBlIHtudW1iZXJ9ICovCiAgICAgICAgLy8gTk9URTogdXNlZCB0byB0cmFjayByZS1lbnRyYW50IGNhbGxzIHRvIGBfZmx1c2hQcm9wZXJ0aWVzYAogICAgICAgIC8vIHBhdGggY2hhbmdlcyBkaXJ0eSBjaGVjayBhZ2FpbnN0IGBfX2RhdGFUZW1wYCBvbmx5IGR1cmluZyBvbmUgInR1cm4iCiAgICAgICAgLy8gYW5kIGFyZSBjbGVhcmVkIHdoZW4gYF9fZGF0YUNvdW50ZXJgIHJldHVybnMgdG8gMC4KICAgICAgICB0aGlzLl9fZGF0YUNvdW50ZXIgPSAwOwogICAgICAgIC8qKiBAdHlwZSB7Ym9vbGVhbn0gKi8KICAgICAgICB0aGlzLl9fZGF0YUNsaWVudHNSZWFkeTsKICAgICAgICAvKiogQHR5cGUge0FycmF5fSAqLwogICAgICAgIHRoaXMuX19kYXRhUGVuZGluZ0NsaWVudHM7CiAgICAgICAgLyoqIEB0eXBlIHtPYmplY3R9ICovCiAgICAgICAgdGhpcy5fX2RhdGFUb05vdGlmeTsKICAgICAgICAvKiogQHR5cGUge09iamVjdH0gKi8KICAgICAgICB0aGlzLl9fZGF0YUxpbmtlZFBhdGhzOwogICAgICAgIC8qKiBAdHlwZSB7Ym9vbGVhbn0gKi8KICAgICAgICB0aGlzLl9fZGF0YUhhc1BhdGhzOwogICAgICAgIC8qKiBAdHlwZSB7T2JqZWN0fSAqLwogICAgICAgIHRoaXMuX19kYXRhQ29tcG91bmRTdG9yYWdlOwogICAgICAgIC8qKiBAdHlwZSB7UG9seW1lcl9Qcm9wZXJ0eUVmZmVjdHN9ICovCiAgICAgICAgdGhpcy5fX2RhdGFIb3N0OwogICAgICAgIC8qKiBAdHlwZSB7IU9iamVjdH0gKi8KICAgICAgICB0aGlzLl9fZGF0YVRlbXA7CiAgICAgICAgLyoqIEB0eXBlIHtib29sZWFufSAqLwogICAgICAgIHRoaXMuX19kYXRhQ2xpZW50c0luaXRpYWxpemVkOwogICAgICAgIC8qKiBAdHlwZSB7IU9iamVjdH0gKi8KICAgICAgICB0aGlzLl9fZGF0YTsKICAgICAgICAvKiogQHR5cGUgeyFPYmplY3R9ICovCiAgICAgICAgdGhpcy5fX2RhdGFQZW5kaW5nOwogICAgICAgIC8qKiBAdHlwZSB7IU9iamVjdH0gKi8KICAgICAgICB0aGlzLl9fZGF0YU9sZDsKICAgICAgICAvKiogQHR5cGUge09iamVjdH0gKi8KICAgICAgICB0aGlzLl9fY29tcHV0ZUVmZmVjdHM7CiAgICAgICAgLyoqIEB0eXBlIHtPYmplY3R9ICovCiAgICAgICAgdGhpcy5fX3JlZmxlY3RFZmZlY3RzOwogICAgICAgIC8qKiBAdHlwZSB7T2JqZWN0fSAqLwogICAgICAgIHRoaXMuX19ub3RpZnlFZmZlY3RzOwogICAgICAgIC8qKiBAdHlwZSB7T2JqZWN0fSAqLwogICAgICAgIHRoaXMuX19wcm9wYWdhdGVFZmZlY3RzOwogICAgICAgIC8qKiBAdHlwZSB7T2JqZWN0fSAqLwogICAgICAgIHRoaXMuX19vYnNlcnZlRWZmZWN0czsKICAgICAgICAvKiogQHR5cGUge09iamVjdH0gKi8KICAgICAgICB0aGlzLl9fcmVhZE9ubHk7CiAgICAgICAgLyoqIEB0eXBlIHshVGVtcGxhdGVJbmZvfSAqLwogICAgICAgIHRoaXMuX190ZW1wbGF0ZUluZm87CiAgICAgIH0KCiAgICAgIGdldCBQUk9QRVJUWV9FRkZFQ1RfVFlQRVMoKSB7CiAgICAgICAgcmV0dXJuIFRZUEVTOwogICAgICB9CgogICAgICAvKioKICAgICAgICogQHJldHVybiB7dm9pZH0KICAgICAgICovCiAgICAgIF9pbml0aWFsaXplUHJvcGVydGllcygpIHsKICAgICAgICBzdXBlci5faW5pdGlhbGl6ZVByb3BlcnRpZXMoKTsKICAgICAgICBob3N0U3RhY2sucmVnaXN0ZXJIb3N0KHRoaXMpOwogICAgICAgIHRoaXMuX19kYXRhQ2xpZW50c1JlYWR5ID0gZmFsc2U7CiAgICAgICAgdGhpcy5fX2RhdGFQZW5kaW5nQ2xpZW50cyA9IG51bGw7CiAgICAgICAgdGhpcy5fX2RhdGFUb05vdGlmeSA9IG51bGw7CiAgICAgICAgdGhpcy5fX2RhdGFMaW5rZWRQYXRocyA9IG51bGw7CiAgICAgICAgdGhpcy5fX2RhdGFIYXNQYXRocyA9IGZhbHNlOwogICAgICAgIC8vIE1heSBiZSBzZXQgb24gaW5zdGFuY2UgcHJpb3IgdG8gdXBncmFkZQogICAgICAgIHRoaXMuX19kYXRhQ29tcG91bmRTdG9yYWdlID0gdGhpcy5fX2RhdGFDb21wb3VuZFN0b3JhZ2UgfHwgbnVsbDsKICAgICAgICB0aGlzLl9fZGF0YUhvc3QgPSB0aGlzLl9fZGF0YUhvc3QgfHwgbnVsbDsKICAgICAgICB0aGlzLl9fZGF0YVRlbXAgPSB7fTsKICAgICAgICB0aGlzLl9fZGF0YUNsaWVudHNJbml0aWFsaXplZCA9IGZhbHNlOwogICAgICB9CgogICAgICAvKioKICAgICAgICogT3ZlcnJpZGVzIGBQb2x5bWVyLlByb3BlcnR5QWNjZXNzb3JzYCBpbXBsZW1lbnRhdGlvbiB0byBwcm92aWRlIGEKICAgICAgICogbW9yZSBlZmZpY2llbnQgaW1wbGVtZW50YXRpb24gb2YgaW5pdGlhbGl6aW5nIHByb3BlcnRpZXMgZnJvbQogICAgICAgKiB0aGUgcHJvdG90eXBlIG9uIHRoZSBpbnN0YW5jZS4KICAgICAgICoKICAgICAgICogQG92ZXJyaWRlCiAgICAgICAqIEBwYXJhbSB7T2JqZWN0fSBwcm9wcyBQcm9wZXJ0aWVzIHRvIGluaXRpYWxpemUgb24gdGhlIHByb3RvdHlwZQogICAgICAgKiBAcmV0dXJuIHt2b2lkfQogICAgICAgKi8KICAgICAgX2luaXRpYWxpemVQcm90b1Byb3BlcnRpZXMocHJvcHMpIHsKICAgICAgICB0aGlzLl9fZGF0YSA9IE9iamVjdC5jcmVhdGUocHJvcHMpOwogICAgICAgIHRoaXMuX19kYXRhUGVuZGluZyA9IE9iamVjdC5jcmVhdGUocHJvcHMpOwogICAgICAgIHRoaXMuX19kYXRhT2xkID0ge307CiAgICAgIH0KCiAgICAgIC8qKgogICAgICAgKiBPdmVycmlkZXMgYFBvbHltZXIuUHJvcGVydHlBY2Nlc3NvcnNgIGltcGxlbWVudGF0aW9uIHRvIGF2b2lkIHNldHRpbmcKICAgICAgICogYF9zZXRQcm9wZXJ0eWAncyBgc2hvdWxkTm90aWZ5OiB0cnVlYC4KICAgICAgICoKICAgICAgICogQG92ZXJyaWRlCiAgICAgICAqIEBwYXJhbSB7T2JqZWN0fSBwcm9wcyBQcm9wZXJ0aWVzIHRvIGluaXRpYWxpemUgb24gdGhlIGluc3RhbmNlCiAgICAgICAqIEByZXR1cm4ge3ZvaWR9CiAgICAgICAqLwogICAgICBfaW5pdGlhbGl6ZUluc3RhbmNlUHJvcGVydGllcyhwcm9wcykgewogICAgICAgIGxldCByZWFkT25seSA9IHRoaXNbVFlQRVMuUkVBRF9PTkxZXTsKICAgICAgICBmb3IgKGxldCBwcm9wIGluIHByb3BzKSB7CiAgICAgICAgICBpZiAoIXJlYWRPbmx5IHx8ICFyZWFkT25seVtwcm9wXSkgewogICAgICAgICAgICB0aGlzLl9fZGF0YVBlbmRpbmcgPSB0aGlzLl9fZGF0YVBlbmRpbmcgfHwge307CiAgICAgICAgICAgIHRoaXMuX19kYXRhT2xkID0gdGhpcy5fX2RhdGFPbGQgfHwge307CiAgICAgICAgICAgIHRoaXMuX19kYXRhW3Byb3BdID0gdGhpcy5fX2RhdGFQZW5kaW5nW3Byb3BdID0gcHJvcHNbcHJvcF07CiAgICAgICAgICB9CiAgICAgICAgfQogICAgICB9CgogICAgICAvLyBQcm90b3R5cGUgc2V0dXAgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKICAgICAgLyoqCiAgICAgICAqIEVxdWl2YWxlbnQgdG8gc3RhdGljIGBhZGRQcm9wZXJ0eUVmZmVjdGAgQVBJIGJ1dCBjYW4gYmUgY2FsbGVkIG9uCiAgICAgICAqIGFuIGluc3RhbmNlIHRvIGFkZCBlZmZlY3RzIGF0IHJ1bnRpbWUuICBTZWUgdGhhdCBtZXRob2QgZm9yCiAgICAgICAqIGZ1bGwgQVBJIGRvY3MuCiAgICAgICAqCiAgICAgICAqIEBwYXJhbSB7c3RyaW5nfSBwcm9wZXJ0eSBQcm9wZXJ0eSB0aGF0IHNob3VsZCB0cmlnZ2VyIHRoZSBlZmZlY3QKICAgICAgICogQHBhcmFtIHtzdHJpbmd9IHR5cGUgRWZmZWN0IHR5cGUsIGZyb20gdGhpcy5QUk9QRVJUWV9FRkZFQ1RfVFlQRVMKICAgICAgICogQHBhcmFtIHtPYmplY3Q9fSBlZmZlY3QgRWZmZWN0IG1ldGFkYXRhIG9iamVjdAogICAgICAgKiBAcmV0dXJuIHt2b2lkfQogICAgICAgKiBAcHJvdGVjdGVkCiAgICAgICAqLwogICAgICBfYWRkUHJvcGVydHlFZmZlY3QocHJvcGVydHksIHR5cGUsIGVmZmVjdCkgewogICAgICAgIHRoaXMuX2NyZWF0ZVByb3BlcnR5QWNjZXNzb3IocHJvcGVydHksIHR5cGUgPT0gVFlQRVMuUkVBRF9PTkxZKTsKICAgICAgICAvLyBlZmZlY3RzIGFyZSBhY2N1bXVsYXRlZCBpbnRvIGFycmF5cyBwZXIgcHJvcGVydHkgYmFzZWQgb24gdHlwZQogICAgICAgIGxldCBlZmZlY3RzID0gZW5zdXJlT3duRWZmZWN0TWFwKHRoaXMsIHR5cGUpW3Byb3BlcnR5XTsKICAgICAgICBpZiAoIWVmZmVjdHMpIHsKICAgICAgICAgIGVmZmVjdHMgPSB0aGlzW3R5cGVdW3Byb3BlcnR5XSA9IFtdOwogICAgICAgIH0KICAgICAgICBlZmZlY3RzLnB1c2goZWZmZWN0KTsKICAgICAgfQoKICAgICAgLyoqCiAgICAgICAqIFJlbW92ZXMgdGhlIGdpdmVuIHByb3BlcnR5IGVmZmVjdC4KICAgICAgICoKICAgICAgICogQHBhcmFtIHtzdHJpbmd9IHByb3BlcnR5IFByb3BlcnR5IHRoZSBlZmZlY3Qgd2FzIGFzc29jaWF0ZWQgd2l0aAogICAgICAgKiBAcGFyYW0ge3N0cmluZ30gdHlwZSBFZmZlY3QgdHlwZSwgZnJvbSB0aGlzLlBST1BFUlRZX0VGRkVDVF9UWVBFUwogICAgICAgKiBAcGFyYW0ge09iamVjdD19IGVmZmVjdCBFZmZlY3QgbWV0YWRhdGEgb2JqZWN0IHRvIHJlbW92ZQogICAgICAgKiBAcmV0dXJuIHt2b2lkfQogICAgICAgKi8KICAgICAgX3JlbW92ZVByb3BlcnR5RWZmZWN0KHByb3BlcnR5LCB0eXBlLCBlZmZlY3QpIHsKICAgICAgICBsZXQgZWZmZWN0cyA9IGVuc3VyZU93bkVmZmVjdE1hcCh0aGlzLCB0eXBlKVtwcm9wZXJ0eV07CiAgICAgICAgbGV0IGlkeCA9IGVmZmVjdHMuaW5kZXhPZihlZmZlY3QpOwogICAgICAgIGlmIChpZHggPj0gMCkgewogICAgICAgICAgZWZmZWN0cy5zcGxpY2UoaWR4LCAxKTsKICAgICAgICB9CiAgICAgIH0KCiAgICAgIC8qKgogICAgICAgKiBSZXR1cm5zIHdoZXRoZXIgdGhlIGN1cnJlbnQgcHJvdG90eXBlL2luc3RhbmNlIGhhcyBhIHByb3BlcnR5IGVmZmVjdAogICAgICAgKiBvZiBhIGNlcnRhaW4gdHlwZS4KICAgICAgICoKICAgICAgICogQHBhcmFtIHtzdHJpbmd9IHByb3BlcnR5IFByb3BlcnR5IG5hbWUKICAgICAgICogQHBhcmFtIHtzdHJpbmc9fSB0eXBlIEVmZmVjdCB0eXBlLCBmcm9tIHRoaXMuUFJPUEVSVFlfRUZGRUNUX1RZUEVTCiAgICAgICAqIEByZXR1cm4ge2Jvb2xlYW59IFRydWUgaWYgdGhlIHByb3RvdHlwZS9pbnN0YW5jZSBoYXMgYW4gZWZmZWN0IG9mIHRoaXMgdHlwZQogICAgICAgKiBAcHJvdGVjdGVkCiAgICAgICAqLwogICAgICBfaGFzUHJvcGVydHlFZmZlY3QocHJvcGVydHksIHR5cGUpIHsKICAgICAgICBsZXQgZWZmZWN0cyA9IHRoaXNbdHlwZV07CiAgICAgICAgcmV0dXJuIEJvb2xlYW4oZWZmZWN0cyAmJiBlZmZlY3RzW3Byb3BlcnR5XSk7CiAgICAgIH0KCiAgICAgIC8qKgogICAgICAgKiBSZXR1cm5zIHdoZXRoZXIgdGhlIGN1cnJlbnQgcHJvdG90eXBlL2luc3RhbmNlIGhhcyBhICJyZWFkIG9ubHkiCiAgICAgICAqIGFjY2Vzc29yIGZvciB0aGUgZ2l2ZW4gcHJvcGVydHkuCiAgICAgICAqCiAgICAgICAqIEBwYXJhbSB7c3RyaW5nfSBwcm9wZXJ0eSBQcm9wZXJ0eSBuYW1lCiAgICAgICAqIEByZXR1cm4ge2Jvb2xlYW59IFRydWUgaWYgdGhlIHByb3RvdHlwZS9pbnN0YW5jZSBoYXMgYW4gZWZmZWN0IG9mIHRoaXMgdHlwZQogICAgICAgKiBAcHJvdGVjdGVkCiAgICAgICAqLwogICAgICBfaGFzUmVhZE9ubHlFZmZlY3QocHJvcGVydHkpIHsKICAgICAgICByZXR1cm4gdGhpcy5faGFzUHJvcGVydHlFZmZlY3QocHJvcGVydHksIFRZUEVTLlJFQURfT05MWSk7CiAgICAgIH0KCiAgICAgIC8qKgogICAgICAgKiBSZXR1cm5zIHdoZXRoZXIgdGhlIGN1cnJlbnQgcHJvdG90eXBlL2luc3RhbmNlIGhhcyBhICJub3RpZnkiCiAgICAgICAqIHByb3BlcnR5IGVmZmVjdCBmb3IgdGhlIGdpdmVuIHByb3BlcnR5LgogICAgICAgKgogICAgICAgKiBAcGFyYW0ge3N0cmluZ30gcHJvcGVydHkgUHJvcGVydHkgbmFtZQogICAgICAgKiBAcmV0dXJuIHtib29sZWFufSBUcnVlIGlmIHRoZSBwcm90b3R5cGUvaW5zdGFuY2UgaGFzIGFuIGVmZmVjdCBvZiB0aGlzIHR5cGUKICAgICAgICogQHByb3RlY3RlZAogICAgICAgKi8KICAgICAgX2hhc05vdGlmeUVmZmVjdChwcm9wZXJ0eSkgewogICAgICAgIHJldHVybiB0aGlzLl9oYXNQcm9wZXJ0eUVmZmVjdChwcm9wZXJ0eSwgVFlQRVMuTk9USUZZKTsKICAgICAgfQoKICAgICAgLyoqCiAgICAgICAqIFJldHVybnMgd2hldGhlciB0aGUgY3VycmVudCBwcm90b3R5cGUvaW5zdGFuY2UgaGFzIGEgInJlZmxlY3QgdG8gYXR0cmlidXRlIgogICAgICAgKiBwcm9wZXJ0eSBlZmZlY3QgZm9yIHRoZSBnaXZlbiBwcm9wZXJ0eS4KICAgICAgICoKICAgICAgICogQHBhcmFtIHtzdHJpbmd9IHByb3BlcnR5IFByb3BlcnR5IG5hbWUKICAgICAgICogQHJldHVybiB7Ym9vbGVhbn0gVHJ1ZSBpZiB0aGUgcHJvdG90eXBlL2luc3RhbmNlIGhhcyBhbiBlZmZlY3Qgb2YgdGhpcyB0eXBlCiAgICAgICAqIEBwcm90ZWN0ZWQKICAgICAgICovCiAgICAgIF9oYXNSZWZsZWN0RWZmZWN0KHByb3BlcnR5KSB7CiAgICAgICAgcmV0dXJuIHRoaXMuX2hhc1Byb3BlcnR5RWZmZWN0KHByb3BlcnR5LCBUWVBFUy5SRUZMRUNUKTsKICAgICAgfQoKICAgICAgLyoqCiAgICAgICAqIFJldHVybnMgd2hldGhlciB0aGUgY3VycmVudCBwcm90b3R5cGUvaW5zdGFuY2UgaGFzIGEgImNvbXB1dGVkIgogICAgICAgKiBwcm9wZXJ0eSBlZmZlY3QgZm9yIHRoZSBnaXZlbiBwcm9wZXJ0eS4KICAgICAgICoKICAgICAgICogQHBhcmFtIHtzdHJpbmd9IHByb3BlcnR5IFByb3BlcnR5IG5hbWUKICAgICAgICogQHJldHVybiB7Ym9vbGVhbn0gVHJ1ZSBpZiB0aGUgcHJvdG90eXBlL2luc3RhbmNlIGhhcyBhbiBlZmZlY3Qgb2YgdGhpcyB0eXBlCiAgICAgICAqIEBwcm90ZWN0ZWQKICAgICAgICovCiAgICAgIF9oYXNDb21wdXRlZEVmZmVjdChwcm9wZXJ0eSkgewogICAgICAgIHJldHVybiB0aGlzLl9oYXNQcm9wZXJ0eUVmZmVjdChwcm9wZXJ0eSwgVFlQRVMuQ09NUFVURSk7CiAgICAgIH0KCiAgICAgIC8vIFJ1bnRpbWUgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKICAgICAgLyoqCiAgICAgICAqIFNldHMgYSBwZW5kaW5nIHByb3BlcnR5IG9yIHBhdGguICBJZiB0aGUgcm9vdCBwcm9wZXJ0eSBvZiB0aGUgcGF0aCBpbgogICAgICAgKiBxdWVzdGlvbiBoYWQgbm8gYWNjZXNzb3IsIHRoZSBwYXRoIGlzIHNldCwgb3RoZXJ3aXNlIGl0IGlzIGVucXVldWVkCiAgICAgICAqIHZpYSBgX3NldFBlbmRpbmdQcm9wZXJ0eWAuCiAgICAgICAqCiAgICAgICAqIFRoaXMgZnVuY3Rpb24gaXNvbGF0ZXMgcmVsYXRpdmVseSBleHBlbnNpdmUgZnVuY3Rpb25hbGl0eSBuZWNlc3NhcnkKICAgICAgICogZm9yIHRoZSBwdWJsaWMgQVBJIChgc2V0YCwgYHNldFByb3BlcnRpZXNgLCBgbm90aWZ5UGF0aGAsIGFuZCBwcm9wZXJ0eQogICAgICAgKiBjaGFuZ2UgbGlzdGVuZXJzIHZpYSB7ey4uLn19IGJpbmRpbmdzKSwgc3VjaCB0aGF0IGl0IGlzIG9ubHkgZG9uZQogICAgICAgKiB3aGVuIHBhdGhzIGVudGVyIHRoZSBzeXN0ZW0sIGFuZCBub3QgYXQgZXZlcnkgcHJvcGFnYXRpb24gc3RlcC4gIEl0CiAgICAgICAqIGFsc28gc2V0cyBhIGBfX2RhdGFIYXNQYXRoc2AgZmxhZyBvbiB0aGUgaW5zdGFuY2Ugd2hpY2ggaXMgdXNlZCB0bwogICAgICAgKiBmYXN0LXBhdGggc2xvd2VyIHBhdGgtbWF0Y2hpbmcgY29kZSBpbiB0aGUgcHJvcGVydHkgZWZmZWN0cyBob3N0IHBhdGhzLgogICAgICAgKgogICAgICAgKiBgcGF0aGAgY2FuIGJlIGEgcGF0aCBzdHJpbmcgb3IgYXJyYXkgb2YgcGF0aCBwYXJ0cyBhcyBhY2NlcHRlZCBieSB0aGUKICAgICAgICogcHVibGljIEFQSS4KICAgICAgICoKICAgICAgICogQHBhcmFtIHtzdHJpbmcgfCAhQXJyYXk8bnVtYmVyfHN0cmluZz59IHBhdGggUGF0aCB0byBzZXQKICAgICAgICogQHBhcmFtIHsqfSB2YWx1ZSBWYWx1ZSB0byBzZXQKICAgICAgICogQHBhcmFtIHtib29sZWFuPX0gc2hvdWxkTm90aWZ5IFNldCB0byB0cnVlIGlmIHRoaXMgY2hhbmdlIHNob3VsZAogICAgICAgKiAgY2F1c2UgYSBwcm9wZXJ0eSBub3RpZmljYXRpb24gZXZlbnQgZGlzcGF0Y2gKICAgICAgICogQHBhcmFtIHtib29sZWFuPX0gaXNQYXRoTm90aWZpY2F0aW9uIElmIHRoZSBwYXRoIGJlaW5nIHNldCBpcyBhIHBhdGgKICAgICAgICogICBub3RpZmljYXRpb24gb2YgYW4gYWxyZWFkeSBjaGFuZ2VkIHZhbHVlLCBhcyBvcHBvc2VkIHRvIGEgcmVxdWVzdAogICAgICAgKiAgIHRvIHNldCBhbmQgbm90aWZ5IHRoZSBjaGFuZ2UuICBJbiB0aGUgbGF0dGVyIGBmYWxzZWAgY2FzZSwgYSBkaXJ0eQogICAgICAgKiAgIGNoZWNrIGlzIHBlcmZvcm1lZCBhbmQgdGhlbiB0aGUgdmFsdWUgaXMgc2V0IHRvIHRoZSBwYXRoIGJlZm9yZQogICAgICAgKiAgIGVucXVldWluZyB0aGUgcGVuZGluZyBwcm9wZXJ0eSBjaGFuZ2UuCiAgICAgICAqIEByZXR1cm4ge2Jvb2xlYW59IFJldHVybnMgdHJ1ZSBpZiB0aGUgcHJvcGVydHkvcGF0aCB3YXMgZW5xdWV1ZWQgaW4KICAgICAgICogICB0aGUgcGVuZGluZyBjaGFuZ2VzIGJhZy4KICAgICAgICogQHByb3RlY3RlZAogICAgICAgKi8KICAgICAgX3NldFBlbmRpbmdQcm9wZXJ0eU9yUGF0aChwYXRoLCB2YWx1ZSwgc2hvdWxkTm90aWZ5LCBpc1BhdGhOb3RpZmljYXRpb24pIHsKICAgICAgICBpZiAoaXNQYXRoTm90aWZpY2F0aW9uIHx8CiAgICAgICAgICAgIFBvbHltZXIuUGF0aC5yb290KEFycmF5LmlzQXJyYXkocGF0aCkgPyBwYXRoWzBdIDogcGF0aCkgIT09IHBhdGgpIHsKICAgICAgICAgIC8vIERpcnR5IGNoZWNrIGNoYW5nZXMgYmVpbmcgc2V0IHRvIGEgcGF0aCBhZ2FpbnN0IHRoZSBhY3R1YWwgb2JqZWN0LAogICAgICAgICAgLy8gc2luY2UgdGhpcyBpcyB0aGUgZW50cnkgcG9pbnQgZm9yIHBhdGhzIGludG8gdGhlIHN5c3RlbTsgZnJvbSBoZXJlCiAgICAgICAgICAvLyB0aGUgb25seSBkaXJ0eSBjaGVja3MgYXJlIGFnYWluc3QgdGhlIGBfX2RhdGFUZW1wYCBjYWNoZSB0byBwcmV2ZW50CiAgICAgICAgICAvLyBkdXBsaWNhdGUgd29yayBpbiB0aGUgc2FtZSB0dXJuIG9ubHkuIE5vdGUsIGlmIHRoaXMgd2FzIGEgbm90aWZpY2F0aW9uCiAgICAgICAgICAvLyBvZiBhIGNoYW5nZSBhbHJlYWR5IHNldCB0byBhIHBhdGggKGlzUGF0aE5vdGlmaWNhdGlvbjogdHJ1ZSksCiAgICAgICAgICAvLyB3ZSBhbHdheXMgbGV0IHRoZSBjaGFuZ2UgdGhyb3VnaCBhbmQgc2tpcCB0aGUgYHNldGAgc2luY2UgaXQgd2FzCiAgICAgICAgICAvLyBhbHJlYWR5IGRpcnR5IGNoZWNrZWQgYXQgdGhlIHBvaW50IG9mIGVudHJ5IGFuZCB0aGUgdW5kZXJseWluZwogICAgICAgICAgLy8gb2JqZWN0IGhhcyBhbHJlYWR5IGJlZW4gdXBkYXRlZAogICAgICAgICAgaWYgKCFpc1BhdGhOb3RpZmljYXRpb24pIHsKICAgICAgICAgICAgbGV0IG9sZCA9IFBvbHltZXIuUGF0aC5nZXQodGhpcywgcGF0aCk7CiAgICAgICAgICAgIHBhdGggPSAvKiogQHR5cGUge3N0cmluZ30gKi8gKFBvbHltZXIuUGF0aC5zZXQodGhpcywgcGF0aCwgdmFsdWUpKTsKICAgICAgICAgICAgLy8gVXNlIHByb3BlcnR5LWFjY2Vzc29yJ3Mgc2ltcGxlciBkaXJ0eSBjaGVjawogICAgICAgICAgICBpZiAoIXBhdGggfHwgIXN1cGVyLl9zaG91bGRQcm9wZXJ0eUNoYW5nZShwYXRoLCB2YWx1ZSwgb2xkKSkgewogICAgICAgICAgICAgIHJldHVybiBmYWxzZTsKICAgICAgICAgICAgfQogICAgICAgICAgfQogICAgICAgICAgdGhpcy5fX2RhdGFIYXNQYXRocyA9IHRydWU7CiAgICAgICAgICBpZiAodGhpcy5fc2V0UGVuZGluZ1Byb3BlcnR5KC8qKkB0eXBle3N0cmluZ30qLyhwYXRoKSwgdmFsdWUsIHNob3VsZE5vdGlmeSkpIHsKICAgICAgICAgICAgY29tcHV0ZUxpbmtlZFBhdGhzKHRoaXMsIHBhdGgsIHZhbHVlKTsKICAgICAgICAgICAgcmV0dXJuIHRydWU7CiAgICAgICAgICB9CiAgICAgICAgfSBlbHNlIHsKICAgICAgICAgIGlmICh0aGlzLl9fZGF0YUhhc0FjY2Vzc29yICYmIHRoaXMuX19kYXRhSGFzQWNjZXNzb3JbcGF0aF0pIHsKICAgICAgICAgICAgcmV0dXJuIHRoaXMuX3NldFBlbmRpbmdQcm9wZXJ0eSgvKipAdHlwZXtzdHJpbmd9Ki8ocGF0aCksIHZhbHVlLCBzaG91bGROb3RpZnkpOwogICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgdGhpc1twYXRoXSA9IHZhbHVlOwogICAgICAgICAgfQogICAgICAgIH0KICAgICAgICByZXR1cm4gZmFsc2U7CiAgICAgIH0KCiAgICAgIC8qKgogICAgICAgKiBBcHBsaWVzIGEgdmFsdWUgdG8gYSBub24tUG9seW1lciBlbGVtZW50L25vZGUncyBwcm9wZXJ0eS4KICAgICAgICoKICAgICAgICogVGhlIGltcGxlbWVudGF0aW9uIG1ha2VzIGEgYmVzdC1lZmZvcnQgYXQgYmluZGluZyBpbnRlcm9wOgogICAgICAgKiBTb21lIG5hdGl2ZSBlbGVtZW50IHByb3BlcnRpZXMgaGF2ZSBzaWRlLWVmZmVjdHMgd2hlbgogICAgICAgKiByZS1zZXR0aW5nIHRoZSBzYW1lIHZhbHVlIChlLmcuIHNldHRpbmcgYDxpbnB1dD4udmFsdWVgIHJlc2V0cyB0aGUKICAgICAgICogY3Vyc29yIHBvc2l0aW9uKSwgc28gd2UgZG8gYSBkaXJ0eS1jaGVjayBiZWZvcmUgc2V0dGluZyB0aGUgdmFsdWUuCiAgICAgICAqIEhvd2V2ZXIsIGZvciBiZXR0ZXIgaW50ZXJvcCB3aXRoIG5vbi1Qb2x5bWVyIGN1c3RvbSBlbGVtZW50cyB0aGF0CiAgICAgICAqIGFjY2VwdCBvYmplY3RzLCB3ZSBleHBsaWNpdGx5IHJlLXNldCBvYmplY3QgY2hhbmdlcyBjb21pbmcgZnJvbSB0aGUKICAgICAgICogUG9seW1lciB3b3JsZCAod2hpY2ggbWF5IGluY2x1ZGUgZGVlcCBvYmplY3QgY2hhbmdlcyB3aXRob3V0IHRoZQogICAgICAgKiB0b3AgcmVmZXJlbmNlIGNoYW5naW5nKSwgZXJyaW5nIG9uIHRoZSBzaWRlIG9mIHByb3ZpZGluZyBtb3JlCiAgICAgICAqIGluZm9ybWF0aW9uLgogICAgICAgKgogICAgICAgKiBVc2VycyBtYXkgb3ZlcnJpZGUgdGhpcyBtZXRob2QgdG8gcHJvdmlkZSBhbHRlcm5hdGUgYXBwcm9hY2hlcy4KICAgICAgICoKICAgICAgICogQHBhcmFtIHshTm9kZX0gbm9kZSBUaGUgbm9kZSB0byBzZXQgYSBwcm9wZXJ0eSBvbgogICAgICAgKiBAcGFyYW0ge3N0cmluZ30gcHJvcCBUaGUgcHJvcGVydHkgdG8gc2V0CiAgICAgICAqIEBwYXJhbSB7Kn0gdmFsdWUgVGhlIHZhbHVlIHRvIHNldAogICAgICAgKiBAcmV0dXJuIHt2b2lkfQogICAgICAgKiBAcHJvdGVjdGVkCiAgICAgICAqLwogICAgICBfc2V0VW5tYW5hZ2VkUHJvcGVydHlUb05vZGUobm9kZSwgcHJvcCwgdmFsdWUpIHsKICAgICAgICAvLyBJdCBpcyBhIGp1ZGdtZW50IGNhbGwgdGhhdCByZXNldHRpbmcgcHJpbWl0aXZlcyBpcwogICAgICAgIC8vICJiYWQiIGFuZCByZXNldHRpbmdzIG9iamVjdHMgaXMgYWxzbyAiZ29vZCI7IGFsdGVybmF0aXZlbHkgd2UgY291bGQKICAgICAgICAvLyBpbXBsZW1lbnQgYSB3aGl0ZWxpc3Qgb2YgdGFnICYgcHJvcGVydHkgdmFsdWVzIHRoYXQgc2hvdWxkIG5ldmVyCiAgICAgICAgLy8gYmUgcmVzZXQgKGUuZy4gPGlucHV0Pi52YWx1ZSAmJiA8c2VsZWN0Pi52YWx1ZSkKICAgICAgICBpZiAodmFsdWUgIT09IG5vZGVbcHJvcF0gfHwgdHlwZW9mIHZhbHVlID09ICdvYmplY3QnKSB7CiAgICAgICAgICBub2RlW3Byb3BdID0gdmFsdWU7CiAgICAgICAgfQogICAgICB9CgogICAgICAvKioKICAgICAgICogT3ZlcnJpZGVzIHRoZSBgUHJvcGVydGllc0NoYW5nZWRgIGltcGxlbWVudGF0aW9uIHRvIGludHJvZHVjZSBzcGVjaWFsCiAgICAgICAqIGRpcnR5IGNoZWNrIGxvZ2ljIGRlcGVuZGluZyBvbiB0aGUgcHJvcGVydHkgJiB2YWx1ZSBiZWluZyBzZXQ6CiAgICAgICAqCiAgICAgICAqIDEuIEFueSB2YWx1ZSBzZXQgdG8gYSBwYXRoIChlLmcuICdvYmoucHJvcCc6IDQyIG9yICdvYmoucHJvcCc6IHsuLi59KQogICAgICAgKiAgICBTdG9yZWQgaW4gYF9fZGF0YVRlbXBgLCBkaXJ0eSBjaGVja2VkIGFnYWluc3QgYF9fZGF0YVRlbXBgCiAgICAgICAqIDIuIE9iamVjdCBzZXQgdG8gc2ltcGxlIHByb3BlcnR5IChlLmcuICdwcm9wJzogey4uLn0pCiAgICAgICAqICAgIFN0b3JlZCBpbiBgX19kYXRhVGVtcGAgYW5kIGBfX2RhdGFgLCBkaXJ0eSBjaGVja2VkIGFnYWluc3QKICAgICAgICogICAgYF9fZGF0YVRlbXBgIGJ5IGRlZmF1bHQgaW1wbGVtZW50YXRpb24gb2YgYF9zaG91bGRQcm9wZXJ0eUNoYW5nZWAKICAgICAgICogMy4gUHJpbWl0aXZlIHZhbHVlIHNldCB0byBzaW1wbGUgcHJvcGVydHkgKGUuZy4gJ3Byb3AnOiA0MikKICAgICAgICogICAgU3RvcmVkIGluIGBfX2RhdGFgLCBkaXJ0eSBjaGVja2VkIGFnYWluc3QgYF9fZGF0YWAKICAgICAgICoKICAgICAgICogVGhlIGRpcnR5LWNoZWNrIGlzIGltcG9ydGFudCB0byBwcmV2ZW50IGN5Y2xlcyBkdWUgdG8gdHdvLXdheQogICAgICAgKiBub3RpZmljYXRpb24sIGJ1dCBwYXRocyBhbmQgb2JqZWN0cyBhcmUgb25seSBkaXJ0eSBjaGVja2VkIGFnYWluc3QgYW55CiAgICAgICAqIHByZXZpb3VzIHZhbHVlIHNldCBkdXJpbmcgdGhpcyB0dXJuIHZpYSBhICJ0ZW1wb3JhcnkgY2FjaGUiIHRoYXQgaXMKICAgICAgICogY2xlYXJlZCB3aGVuIHRoZSBsYXN0IGBfcHJvcGVydGllc0NoYW5nZWRgIGV4aXRzLiBUaGlzIGlzIHNvOgogICAgICAgKiBhLiBhbnkgY2FjaGVkIGFycmF5IHBhdGhzIChlLmcuICdhcnJheS4zLnByb3AnKSBtYXkgYmUgaW52YWxpZGF0ZWQKICAgICAgICogICAgZHVlIHRvIGFycmF5IG11dGF0aW9ucyBsaWtlIHNoaWZ0L3Vuc2hpZnQvc3BsaWNlOyB0aGlzIGlzIGZpbmUKICAgICAgICogICAgc2luY2UgcGF0aCBjaGFuZ2VzIGFyZSBkaXJ0eS1jaGVja2VkIGF0IHVzZXIgZW50cnkgcG9pbnRzIGxpa2UgYHNldGAKICAgICAgICogYi4gZGlydHktY2hlY2tpbmcgZm9yIG9iamVjdHMgb25seSBsYXN0cyBvbmUgdHVybiB0byBhbGxvdyB0aGUgdXNlcgogICAgICAgKiAgICB0byBtdXRhdGUgdGhlIG9iamVjdCBpbi1wbGFjZSBhbmQgcmUtc2V0IGl0IHdpdGggdGhlIHNhbWUgaWRlbnRpdHkKICAgICAgICogICAgYW5kIGhhdmUgYWxsIHN1Yi1wcm9wZXJ0aWVzIHJlLXByb3BhZ2F0ZWQgaW4gYSBzdWJzZXF1ZW50IHR1cm4uCiAgICAgICAqCiAgICAgICAqIFRoZSB0ZW1wIGNhY2hlIGlzIG5vdCBuZWNlc3NhcmlseSBzdWZmaWNpZW50IHRvIHByZXZlbnQgaW52YWxpZCBhcnJheQogICAgICAgKiBwYXRocywgc2luY2UgYSBzcGxpY2UgY2FuIGhhcHBlbiBkdXJpbmcgdGhlIHNhbWUgdHVybiAod2l0aCBwYXRob2xvZ2ljYWwKICAgICAgICogdXNlciBjb2RlKTsgd2UgY291bGQgaW50cm9kdWNlIGEgImZpeHVwIiBmb3IgdGVtcG9yYXJpbHkgY2FjaGVkIGFycmF5CiAgICAgICAqIHBhdGhzIGlmIG5lZWRlZDogaHR0cHM6Ly9naXRodWIuY29tL1BvbHltZXIvcG9seW1lci9pc3N1ZXMvNDIyNwogICAgICAgKgogICAgICAgKiBAb3ZlcnJpZGUKICAgICAgICogQHBhcmFtIHtzdHJpbmd9IHByb3BlcnR5IE5hbWUgb2YgdGhlIHByb3BlcnR5CiAgICAgICAqIEBwYXJhbSB7Kn0gdmFsdWUgVmFsdWUgdG8gc2V0CiAgICAgICAqIEBwYXJhbSB7Ym9vbGVhbj19IHNob3VsZE5vdGlmeSBUcnVlIGlmIHByb3BlcnR5IHNob3VsZCBmaXJlIG5vdGlmaWNhdGlvbgogICAgICAgKiAgIGV2ZW50IChhcHBsaWVzIG9ubHkgZm9yIGBub3RpZnk6IHRydWVgIHByb3BlcnRpZXMpCiAgICAgICAqIEByZXR1cm4ge2Jvb2xlYW59IFJldHVybnMgdHJ1ZSBpZiB0aGUgcHJvcGVydHkgY2hhbmdlZAogICAgICAgKi8KICAgICAgX3NldFBlbmRpbmdQcm9wZXJ0eShwcm9wZXJ0eSwgdmFsdWUsIHNob3VsZE5vdGlmeSkgewogICAgICAgIGxldCBpc1BhdGggPSB0aGlzLl9fZGF0YUhhc1BhdGhzICYmIFBvbHltZXIuUGF0aC5pc1BhdGgocHJvcGVydHkpOwogICAgICAgIGxldCBwcmV2UHJvcHMgPSBpc1BhdGggPyB0aGlzLl9fZGF0YVRlbXAgOiB0aGlzLl9fZGF0YTsKICAgICAgICBpZiAodGhpcy5fc2hvdWxkUHJvcGVydHlDaGFuZ2UocHJvcGVydHksIHZhbHVlLCBwcmV2UHJvcHNbcHJvcGVydHldKSkgewogICAgICAgICAgaWYgKCF0aGlzLl9fZGF0YVBlbmRpbmcpIHsKICAgICAgICAgICAgdGhpcy5fX2RhdGFQZW5kaW5nID0ge307CiAgICAgICAgICAgIHRoaXMuX19kYXRhT2xkID0ge307CiAgICAgICAgICB9CiAgICAgICAgICAvLyBFbnN1cmUgb2xkIGlzIGNhcHR1cmVkIGZyb20gdGhlIGxhc3QgdHVybgogICAgICAgICAgaWYgKCEocHJvcGVydHkgaW4gdGhpcy5fX2RhdGFPbGQpKSB7CiAgICAgICAgICAgIHRoaXMuX19kYXRhT2xkW3Byb3BlcnR5XSA9IHRoaXMuX19kYXRhW3Byb3BlcnR5XTsKICAgICAgICAgIH0KICAgICAgICAgIC8vIFBhdGhzIGFyZSBzdG9yZWQgaW4gdGVtcG9yYXJ5IGNhY2hlIChjbGVhcmVkIGF0IGVuZCBvZiB0dXJuKSwKICAgICAgICAgIC8vIHdoaWNoIGlzIHVzZWQgZm9yIGRpcnR5LWNoZWNraW5nLCBhbGwgb3RoZXJzIHN0b3JlZCBpbiBfX2RhdGEKICAgICAgICAgIGlmIChpc1BhdGgpIHsKICAgICAgICAgICAgdGhpcy5fX2RhdGFUZW1wW3Byb3BlcnR5XSA9IHZhbHVlOwogICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgdGhpcy5fX2RhdGFbcHJvcGVydHldID0gdmFsdWU7CiAgICAgICAgICB9CiAgICAgICAgICAvLyBBbGwgY2hhbmdlcyBnbyBpbnRvIHBlbmRpbmcgcHJvcGVydHkgYmFnLCBwYXNzZWQgdG8gX3Byb3BlcnRpZXNDaGFuZ2VkCiAgICAgICAgICB0aGlzLl9fZGF0YVBlbmRpbmdbcHJvcGVydHldID0gdmFsdWU7CiAgICAgICAgICAvLyBUcmFjayBwcm9wZXJ0aWVzIHRoYXQgc2hvdWxkIG5vdGlmeSBzZXBhcmF0ZWx5CiAgICAgICAgICBpZiAoaXNQYXRoIHx8ICh0aGlzW1RZUEVTLk5PVElGWV0gJiYgdGhpc1tUWVBFUy5OT1RJRlldW3Byb3BlcnR5XSkpIHsKICAgICAgICAgICAgdGhpcy5fX2RhdGFUb05vdGlmeSA9IHRoaXMuX19kYXRhVG9Ob3RpZnkgfHwge307CiAgICAgICAgICAgIHRoaXMuX19kYXRhVG9Ob3RpZnlbcHJvcGVydHldID0gc2hvdWxkTm90aWZ5OwogICAgICAgICAgfQogICAgICAgICAgcmV0dXJuIHRydWU7CiAgICAgICAgfQogICAgICAgIHJldHVybiBmYWxzZTsKICAgICAgfQoKICAgICAgLyoqCiAgICAgICAqIE92ZXJyaWRlcyBiYXNlIGltcGxlbWVudGF0aW9uIHRvIGVuc3VyZSBhbGwgYWNjZXNzb3JzIHNldCBgc2hvdWxkTm90aWZ5YAogICAgICAgKiB0byB0cnVlLCBmb3IgcGVyLXByb3BlcnR5IG5vdGlmaWNhdGlvbiB0cmFja2luZy4KICAgICAgICoKICAgICAgICogQG92ZXJyaWRlCiAgICAgICAqIEBwYXJhbSB7c3RyaW5nfSBwcm9wZXJ0eSBOYW1lIG9mIHRoZSBwcm9wZXJ0eQogICAgICAgKiBAcGFyYW0geyp9IHZhbHVlIFZhbHVlIHRvIHNldAogICAgICAgKiBAcmV0dXJuIHt2b2lkfQogICAgICAgKi8KICAgICAgX3NldFByb3BlcnR5KHByb3BlcnR5LCB2YWx1ZSkgewogICAgICAgIGlmICh0aGlzLl9zZXRQZW5kaW5nUHJvcGVydHkocHJvcGVydHksIHZhbHVlLCB0cnVlKSkgewogICAgICAgICAgdGhpcy5faW52YWxpZGF0ZVByb3BlcnRpZXMoKTsKICAgICAgICB9CiAgICAgIH0KCiAgICAgIC8qKgogICAgICAgKiBPdmVycmlkZXMgYFByb3BlcnR5QWNjZXNzb3JgJ3MgZGVmYXVsdCBhc3luYyBxdWV1aW5nIG9mCiAgICAgICAqIGBfcHJvcGVydGllc0NoYW5nZWRgOiBpZiBgX19kYXRhUmVhZHlgIGlzIGZhbHNlIChoYXMgbm90IHlldCBiZWVuCiAgICAgICAqIG1hbnVhbGx5IGZsdXNoZWQpLCB0aGUgZnVuY3Rpb24gbm8tb3BzOyBvdGhlcndpc2UgZmx1c2hlcwogICAgICAgKiBgX3Byb3BlcnRpZXNDaGFuZ2VkYCBzeW5jaHJvbm91c2x5LgogICAgICAgKgogICAgICAgKiBAb3ZlcnJpZGUKICAgICAgICogQHJldHVybiB7dm9pZH0KICAgICAgICovCiAgICAgIF9pbnZhbGlkYXRlUHJvcGVydGllcygpIHsKICAgICAgICBpZiAodGhpcy5fX2RhdGFSZWFkeSkgewogICAgICAgICAgdGhpcy5fZmx1c2hQcm9wZXJ0aWVzKCk7CiAgICAgICAgfQogICAgICB9CgogICAgICAvKioKICAgICAgICogRW5xdWV1ZXMgdGhlIGdpdmVuIGNsaWVudCBvbiBhIGxpc3Qgb2YgcGVuZGluZyBjbGllbnRzLCB3aG9zZQogICAgICAgKiBwZW5kaW5nIHByb3BlcnR5IGNoYW5nZXMgY2FuIGxhdGVyIGJlIGZsdXNoZWQgdmlhIGEgY2FsbCB0bwogICAgICAgKiBgX2ZsdXNoQ2xpZW50c2AuCiAgICAgICAqCiAgICAgICAqIEBwYXJhbSB7T2JqZWN0fSBjbGllbnQgUHJvcGVydHlFZmZlY3RzIGNsaWVudCB0byBlbnF1ZXVlCiAgICAgICAqIEByZXR1cm4ge3ZvaWR9CiAgICAgICAqIEBwcm90ZWN0ZWQKICAgICAgICovCiAgICAgIF9lbnF1ZXVlQ2xpZW50KGNsaWVudCkgewogICAgICAgIHRoaXMuX19kYXRhUGVuZGluZ0NsaWVudHMgPSB0aGlzLl9fZGF0YVBlbmRpbmdDbGllbnRzIHx8IFtdOwogICAgICAgIGlmIChjbGllbnQgIT09IHRoaXMpIHsKICAgICAgICAgIHRoaXMuX19kYXRhUGVuZGluZ0NsaWVudHMucHVzaChjbGllbnQpOwogICAgICAgIH0KICAgICAgfQoKICAgICAgLyoqCiAgICAgICAqIE92ZXJyaWRlcyBzdXBlcmNsYXNzIGltcGxlbWVudGF0aW9uLgogICAgICAgKgogICAgICAgKiBAcmV0dXJuIHt2b2lkfQogICAgICAgKiBAcHJvdGVjdGVkCiAgICAgICAqLwogICAgICBfZmx1c2hQcm9wZXJ0aWVzKCkgewogICAgICAgIHRoaXMuX19kYXRhQ291bnRlcisrOwogICAgICAgIHN1cGVyLl9mbHVzaFByb3BlcnRpZXMoKTsKICAgICAgICB0aGlzLl9fZGF0YUNvdW50ZXItLTsKICAgICAgfQoKICAgICAgLyoqCiAgICAgICAqIEZsdXNoZXMgYW55IGNsaWVudHMgcHJldmlvdXNseSBlbnF1ZXVlZCB2aWEgYF9lbnF1ZXVlQ2xpZW50YCwgY2F1c2luZwogICAgICAgKiB0aGVpciBgX2ZsdXNoUHJvcGVydGllc2AgbWV0aG9kIHRvIHJ1bi4KICAgICAgICoKICAgICAgICogQHJldHVybiB7dm9pZH0KICAgICAgICogQHByb3RlY3RlZAogICAgICAgKi8KICAgICAgX2ZsdXNoQ2xpZW50cygpIHsKICAgICAgICBpZiAoIXRoaXMuX19kYXRhQ2xpZW50c1JlYWR5KSB7CiAgICAgICAgICB0aGlzLl9fZGF0YUNsaWVudHNSZWFkeSA9IHRydWU7CiAgICAgICAgICB0aGlzLl9yZWFkeUNsaWVudHMoKTsKICAgICAgICAgIC8vIE92ZXJyaWRlIHBvaW50IHdoZXJlIGFjY2Vzc29ycyBhcmUgdHVybmVkIG9uOyBpbXBvcnRhbnRseSwKICAgICAgICAgIC8vIHRoaXMgaXMgYWZ0ZXIgY2xpZW50cyBoYXZlIGZ1bGx5IHJlYWRpZWQsIHByb3ZpZGluZyBhIGd1YXJhbnRlZQogICAgICAgICAgLy8gdGhhdCBhbnkgcHJvcGVydHkgZWZmZWN0cyBvY2N1ciBvbmx5IGFmdGVyIGFsbCBjbGllbnRzIGFyZSByZWFkeS4KICAgICAgICAgIHRoaXMuX19kYXRhUmVhZHkgPSB0cnVlOwogICAgICAgIH0gZWxzZSB7CiAgICAgICAgICB0aGlzLl9fZW5hYmxlT3JGbHVzaENsaWVudHMoKTsKICAgICAgICB9CiAgICAgIH0KCiAgICAgIC8vIE5PVEU6IFdlIGVuc3VyZSBjbGllbnRzIGVpdGhlciBlbmFibGUgb3IgZmx1c2ggYXMgYXBwcm9wcmlhdGUuIFRoaXMKICAgICAgLy8gaGFuZGxlcyB0d28gY29ybmVyIGNhc2VzOgogICAgICAvLyAoMSkgY2xpZW50cyBmbHVzaCBwcm9wZXJseSB3aGVuIGNvbm5lY3RlZC9lbmFibGVkIGJlZm9yZSB0aGUgaG9zdAogICAgICAvLyBlbmFibGVzOyBlLmcuCiAgICAgIC8vICAgKGEpIFRlbXBsYXRpemUgc3RhbXBzIHdpdGggbm8gcHJvcGVydGllcyBhbmQgZG9lcyBub3QgZmx1c2ggYW5kCiAgICAgIC8vICAgKGIpIHRoZSBpbnN0YW5jZSBpcyBpbnNlcnRlZCBpbnRvIGRvbSBhbmQKICAgICAgLy8gICAoYykgdGhlbiB0aGUgaW5zdGFuY2UgZmx1c2hlcy4KICAgICAgLy8gKDIpIGNsaWVudHMgZW5hYmxlIHByb3Blcmx5IHdoZW4gbm90IGNvbm5lY3RlZC9lbmFibGVkIHdoZW4gdGhlIGhvc3QKICAgICAgLy8gZmx1c2hlczsgZS5nLgogICAgICAvLyAgIChhKSBhIHRlbXBsYXRlIGlzIHJ1bnRpbWUgc3RhbXBlZCBhbmQgbm90IHlldCBjb25uZWN0ZWQvZW5hYmxlZAogICAgICAvLyAgIChiKSBhIGhvc3Qgc2V0cyBhIHByb3BlcnR5LCBjYXVzaW5nIHN0YW1wZWQgZG9tIHRvIGZsdXNoCiAgICAgIC8vICAgKGMpIHRoZSBzdGFtcGVkIGRvbSBlbmFibGVzLgogICAgICBfX2VuYWJsZU9yRmx1c2hDbGllbnRzKCkgewogICAgICAgIGxldCBjbGllbnRzID0gdGhpcy5fX2RhdGFQZW5kaW5nQ2xpZW50czsKICAgICAgICBpZiAoY2xpZW50cykgewogICAgICAgICAgdGhpcy5fX2RhdGFQZW5kaW5nQ2xpZW50cyA9IG51bGw7CiAgICAgICAgICBmb3IgKGxldCBpPTA7IGkgPCBjbGllbnRzLmxlbmd0aDsgaSsrKSB7CiAgICAgICAgICAgIGxldCBjbGllbnQgPSBjbGllbnRzW2ldOwogICAgICAgICAgICBpZiAoIWNsaWVudC5fX2RhdGFFbmFibGVkKSB7CiAgICAgICAgICAgICAgY2xpZW50Ll9lbmFibGVQcm9wZXJ0aWVzKCk7CiAgICAgICAgICAgIH0gZWxzZSBpZiAoY2xpZW50Ll9fZGF0YVBlbmRpbmcpIHsKICAgICAgICAgICAgICBjbGllbnQuX2ZsdXNoUHJvcGVydGllcygpOwogICAgICAgICAgICB9CiAgICAgICAgICB9CiAgICAgICAgfQogICAgICB9CgogICAgICAvKioKICAgICAgICogUGVyZm9ybSBhbnkgaW5pdGlhbCBzZXR1cCBvbiBjbGllbnQgZG9tLiBDYWxsZWQgYmVmb3JlIHRoZSBmaXJzdAogICAgICAgKiBgX2ZsdXNoUHJvcGVydGllc2AgY2FsbCBvbiBjbGllbnQgZG9tIGFuZCBiZWZvcmUgYW55IGVsZW1lbnQKICAgICAgICogb2JzZXJ2ZXJzIGFyZSBjYWxsZWQuCiAgICAgICAqCiAgICAgICAqIEByZXR1cm4ge3ZvaWR9CiAgICAgICAqIEBwcm90ZWN0ZWQKICAgICAgICovCiAgICAgIF9yZWFkeUNsaWVudHMoKSB7CiAgICAgICAgdGhpcy5fX2VuYWJsZU9yRmx1c2hDbGllbnRzKCk7CiAgICAgIH0KCiAgICAgIC8qKgogICAgICAgKiBTZXRzIGEgYmFnIG9mIHByb3BlcnR5IGNoYW5nZXMgdG8gdGhpcyBpbnN0YW5jZSwgYW5kCiAgICAgICAqIHN5bmNocm9ub3VzbHkgcHJvY2Vzc2VzIGFsbCBlZmZlY3RzIG9mIHRoZSBwcm9wZXJ0aWVzIGFzIGEgYmF0Y2guCiAgICAgICAqCiAgICAgICAqIFByb3BlcnR5IG5hbWVzIG11c3QgYmUgc2ltcGxlIHByb3BlcnRpZXMsIG5vdCBwYXRocy4gIEJhdGNoZWQKICAgICAgICogcGF0aCBwcm9wYWdhdGlvbiBpcyBub3Qgc3VwcG9ydGVkLgogICAgICAgKgogICAgICAgKiBAcGFyYW0ge09iamVjdH0gcHJvcHMgQmFnIG9mIG9uZSBvciBtb3JlIGtleS12YWx1ZSBwYWlycyB3aG9zZSBrZXkgaXMKICAgICAgICogICBhIHByb3BlcnR5IGFuZCB2YWx1ZSBpcyB0aGUgbmV3IHZhbHVlIHRvIHNldCBmb3IgdGhhdCBwcm9wZXJ0eS4KICAgICAgICogQHBhcmFtIHtib29sZWFuPX0gc2V0UmVhZE9ubHkgV2hlbiB0cnVlLCBhbnkgcHJpdmF0ZSB2YWx1ZXMgc2V0IGluCiAgICAgICAqICAgYHByb3BzYCB3aWxsIGJlIHNldC4gQnkgZGVmYXVsdCwgYHNldFByb3BlcnRpZXNgIHdpbGwgbm90IHNldAogICAgICAgKiAgIGByZWFkT25seTogdHJ1ZWAgcm9vdCBwcm9wZXJ0aWVzLgogICAgICAgKiBAcmV0dXJuIHt2b2lkfQogICAgICAgKiBAcHVibGljCiAgICAgICAqLwogICAgICBzZXRQcm9wZXJ0aWVzKHByb3BzLCBzZXRSZWFkT25seSkgewogICAgICAgIGZvciAobGV0IHBhdGggaW4gcHJvcHMpIHsKICAgICAgICAgIGlmIChzZXRSZWFkT25seSB8fCAhdGhpc1tUWVBFUy5SRUFEX09OTFldIHx8ICF0aGlzW1RZUEVTLlJFQURfT05MWV1bcGF0aF0pIHsKICAgICAgICAgICAgLy9UT0RPKGtzY2hhYWYpOiBleHBsaWNpdGx5IGRpc2FsbG93IHBhdGhzIGluIHNldFByb3BlcnR5PwogICAgICAgICAgICAvLyB3aWxkY2FyZCBvYnNlcnZlcnMgY3VycmVudGx5IG9ubHkgcGFzcyB0aGUgZmlyc3QgY2hhbmdlZCBwYXRoCiAgICAgICAgICAgIC8vIGluIHRoZSBgaW5mb2Agb2JqZWN0LCBhbmQgeW91IGNvdWxkIGRvIHNvbWUgb2RkIHRoaW5ncyBiYXRjaGluZwogICAgICAgICAgICAvLyBwYXRocywgZS5nLiB7J2Zvby5iYXInOiB7Li4ufSwgJ2Zvbyc6IG51bGx9CiAgICAgICAgICAgIHRoaXMuX3NldFBlbmRpbmdQcm9wZXJ0eU9yUGF0aChwYXRoLCBwcm9wc1twYXRoXSwgdHJ1ZSk7CiAgICAgICAgICB9CiAgICAgICAgfQogICAgICAgIHRoaXMuX2ludmFsaWRhdGVQcm9wZXJ0aWVzKCk7CiAgICAgIH0KCiAgICAgIC8qKgogICAgICAgKiBPdmVycmlkZXMgYFByb3BlcnR5QWNjZXNzb3JzYCBzbyB0aGF0IHByb3BlcnR5IGFjY2Vzc29yCiAgICAgICAqIHNpZGUgZWZmZWN0cyBhcmUgbm90IGVuYWJsZWQgdW50aWwgYWZ0ZXIgY2xpZW50IGRvbSBpcyBmdWxseSByZWFkeS4KICAgICAgICogQWxzbyBjYWxscyBgX2ZsdXNoQ2xpZW50c2AgY2FsbGJhY2sgdG8gZW5zdXJlIGNsaWVudCBkb20gaXMgZW5hYmxlZAogICAgICAgKiB0aGF0IHdhcyBub3QgZW5hYmxlZCBhcyBhIHJlc3VsdCBvZiBmbHVzaGluZyBwcm9wZXJ0aWVzLgogICAgICAgKgogICAgICAgKiBAb3ZlcnJpZGUKICAgICAgICogQHJldHVybiB7dm9pZH0KICAgICAgICovCiAgICAgIHJlYWR5KCkgewogICAgICAgIC8vIEl0IGlzIGltcG9ydGFudCB0aGF0IGBzdXBlci5yZWFkeSgpYCBpcyBub3QgY2FsbGVkIGhlcmUgYXMgaXQKICAgICAgICAvLyBpbW1lZGlhdGVseSB0dXJucyBvbiBhY2Nlc3NvcnMuIEluc3RlYWQsIHdlIHdhaXQgdW50aWwgYHJlYWR5Q2xpZW50c2AKICAgICAgICAvLyB0byBlbmFibGUgYWNjZXNzb3JzIHRvIHByb3ZpZGUgYSBndWFyYW50ZWUgdGhhdCBjbGllbnRzIGFyZSByZWFkeQogICAgICAgIC8vIGJlZm9yZSBwcm9jZXNzaW5nIGFueSBhY2Nlc3NvcnMgc2lkZSBlZmZlY3RzLgogICAgICAgIHRoaXMuX2ZsdXNoUHJvcGVydGllcygpOwogICAgICAgIC8vIElmIG5vIGRhdGEgd2FzIHBlbmRpbmcsIGBfZmx1c2hQcm9wZXJ0aWVzYCB3aWxsIG5vdCBgZmx1c2hDbGllbnRzYAogICAgICAgIC8vIHNvIGVuc3VyZSB0aGlzIGlzIGRvbmUuCiAgICAgICAgaWYgKCF0aGlzLl9fZGF0YUNsaWVudHNSZWFkeSkgewogICAgICAgICAgdGhpcy5fZmx1c2hDbGllbnRzKCk7CiAgICAgICAgfQogICAgICAgIC8vIEJlZm9yZSByZWFkeSwgY2xpZW50IG5vdGlmaWNhdGlvbnMgZG8gbm90IHRyaWdnZXIgX2ZsdXNoUHJvcGVydGllcy4KICAgICAgICAvLyBUaGVyZWZvcmUgYSBmbHVzaCBpcyBuZWNlc3NhcnkgaGVyZSBpZiBkYXRhIGhhcyBiZWVuIHNldC4KICAgICAgICBpZiAodGhpcy5fX2RhdGFQZW5kaW5nKSB7CiAgICAgICAgICB0aGlzLl9mbHVzaFByb3BlcnRpZXMoKTsKICAgICAgICB9CiAgICAgIH0KCiAgICAgIC8qKgogICAgICAgKiBJbXBsZW1lbnRzIGBQcm9wZXJ0eUFjY2Vzc29yc2AncyBwcm9wZXJ0aWVzIGNoYW5nZWQgY2FsbGJhY2suCiAgICAgICAqCiAgICAgICAqIFJ1bnMgZWFjaCBjbGFzcyBvZiBlZmZlY3RzIGZvciB0aGUgYmF0Y2ggb2YgY2hhbmdlZCBwcm9wZXJ0aWVzIGluCiAgICAgICAqIGEgc3BlY2lmaWMgb3JkZXIgKGNvbXB1dGUsIHByb3BhZ2F0ZSwgcmVmbGVjdCwgb2JzZXJ2ZSwgbm90aWZ5KS4KICAgICAgICoKICAgICAgICogQHBhcmFtIHshT2JqZWN0fSBjdXJyZW50UHJvcHMgQmFnIG9mIGFsbCBjdXJyZW50IGFjY2Vzc29yIHZhbHVlcwogICAgICAgKiBAcGFyYW0geyFPYmplY3R9IGNoYW5nZWRQcm9wcyBCYWcgb2YgcHJvcGVydGllcyBjaGFuZ2VkIHNpbmNlIHRoZSBsYXN0CiAgICAgICAqICAgY2FsbCB0byBgX3Byb3BlcnRpZXNDaGFuZ2VkYAogICAgICAgKiBAcGFyYW0geyFPYmplY3R9IG9sZFByb3BzIEJhZyBvZiBwcmV2aW91cyB2YWx1ZXMgZm9yIGVhY2ggcHJvcGVydHkKICAgICAgICogICBpbiBgY2hhbmdlZFByb3BzYAogICAgICAgKiBAcmV0dXJuIHt2b2lkfQogICAgICAgKi8KICAgICAgX3Byb3BlcnRpZXNDaGFuZ2VkKGN1cnJlbnRQcm9wcywgY2hhbmdlZFByb3BzLCBvbGRQcm9wcykgewogICAgICAgIC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KICAgICAgICAvLyBsZXQgYyA9IE9iamVjdC5nZXRPd25Qcm9wZXJ0eU5hbWVzKGNoYW5nZWRQcm9wcyB8fCB7fSk7CiAgICAgICAgLy8gd2luZG93LmRlYnVnICYmIGNvbnNvbGUuZ3JvdXAodGhpcy5sb2NhbE5hbWUgKyAnIycgKyB0aGlzLmlkICsgJzogJyArIGMpOwogICAgICAgIC8vIGlmICh3aW5kb3cuZGVidWcpIHsgZGVidWdnZXI7IH0KICAgICAgICAvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiAgICAgICAgbGV0IGhhc1BhdGhzID0gdGhpcy5fX2RhdGFIYXNQYXRoczsKICAgICAgICB0aGlzLl9fZGF0YUhhc1BhdGhzID0gZmFsc2U7CiAgICAgICAgLy8gQ29tcHV0ZSBwcm9wZXJ0aWVzCiAgICAgICAgcnVuQ29tcHV0ZWRFZmZlY3RzKHRoaXMsIGNoYW5nZWRQcm9wcywgb2xkUHJvcHMsIGhhc1BhdGhzKTsKICAgICAgICAvLyBDbGVhciBub3RpZnkgcHJvcGVydGllcyBwcmlvciB0byBwb3NzaWJsZSByZWVudHJ5IChwcm9wYWdhdGUsIG9ic2VydmUpLAogICAgICAgIC8vIGJ1dCBhZnRlciBjb21wdXRpbmcgZWZmZWN0cyBoYXZlIGEgY2hhbmNlIHRvIGFkZCB0byB0aGVtCiAgICAgICAgbGV0IG5vdGlmeVByb3BzID0gdGhpcy5fX2RhdGFUb05vdGlmeTsKICAgICAgICB0aGlzLl9fZGF0YVRvTm90aWZ5ID0gbnVsbDsKICAgICAgICAvLyBQcm9wYWdhdGUgcHJvcGVydGllcyB0byBjbGllbnRzCiAgICAgICAgdGhpcy5fcHJvcGFnYXRlUHJvcGVydHlDaGFuZ2VzKGNoYW5nZWRQcm9wcywgb2xkUHJvcHMsIGhhc1BhdGhzKTsKICAgICAgICAvLyBGbHVzaCBjbGllbnRzCiAgICAgICAgdGhpcy5fZmx1c2hDbGllbnRzKCk7CiAgICAgICAgLy8gUmVmbGVjdCBwcm9wZXJ0aWVzCiAgICAgICAgcnVuRWZmZWN0cyh0aGlzLCB0aGlzW1RZUEVTLlJFRkxFQ1RdLCBjaGFuZ2VkUHJvcHMsIG9sZFByb3BzLCBoYXNQYXRocyk7CiAgICAgICAgLy8gT2JzZXJ2ZSBwcm9wZXJ0aWVzCiAgICAgICAgcnVuRWZmZWN0cyh0aGlzLCB0aGlzW1RZUEVTLk9CU0VSVkVdLCBjaGFuZ2VkUHJvcHMsIG9sZFByb3BzLCBoYXNQYXRocyk7CiAgICAgICAgLy8gTm90aWZ5IHByb3BlcnRpZXMgdG8gaG9zdAogICAgICAgIGlmIChub3RpZnlQcm9wcykgewogICAgICAgICAgcnVuTm90aWZ5RWZmZWN0cyh0aGlzLCBub3RpZnlQcm9wcywgY2hhbmdlZFByb3BzLCBvbGRQcm9wcywgaGFzUGF0aHMpOwogICAgICAgIH0KICAgICAgICAvLyBDbGVhciB0ZW1wb3JhcnkgY2FjaGUgYXQgZW5kIG9mIHR1cm4KICAgICAgICBpZiAodGhpcy5fX2RhdGFDb3VudGVyID09IDEpIHsKICAgICAgICAgIHRoaXMuX19kYXRhVGVtcCA9IHt9OwogICAgICAgIH0KICAgICAgICAvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiAgICAgICAgLy8gd2luZG93LmRlYnVnICYmIGNvbnNvbGUuZ3JvdXBFbmQodGhpcy5sb2NhbE5hbWUgKyAnIycgKyB0aGlzLmlkICsgJzogJyArIGMpOwogICAgICAgIC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KICAgICAgfQoKICAgICAgLyoqCiAgICAgICAqIENhbGxlZCB0byBwcm9wYWdhdGUgYW55IHByb3BlcnR5IGNoYW5nZXMgdG8gc3RhbXBlZCB0ZW1wbGF0ZSBub2RlcwogICAgICAgKiBtYW5hZ2VkIGJ5IHRoaXMgZWxlbWVudC4KICAgICAgICoKICAgICAgICogQHBhcmFtIHtPYmplY3R9IGNoYW5nZWRQcm9wcyBCYWcgb2YgY2hhbmdlZCBwcm9wZXJ0aWVzCiAgICAgICAqIEBwYXJhbSB7T2JqZWN0fSBvbGRQcm9wcyBCYWcgb2YgcHJldmlvdXMgdmFsdWVzIGZvciBjaGFuZ2VkIHByb3BlcnRpZXMKICAgICAgICogQHBhcmFtIHtib29sZWFufSBoYXNQYXRocyBUcnVlIHdpdGggYHByb3BzYCBjb250YWlucyBvbmUgb3IgbW9yZSBwYXRocwogICAgICAgKiBAcmV0dXJuIHt2b2lkfQogICAgICAgKiBAcHJvdGVjdGVkCiAgICAgICAqLwogICAgICBfcHJvcGFnYXRlUHJvcGVydHlDaGFuZ2VzKGNoYW5nZWRQcm9wcywgb2xkUHJvcHMsIGhhc1BhdGhzKSB7CiAgICAgICAgaWYgKHRoaXNbVFlQRVMuUFJPUEFHQVRFXSkgewogICAgICAgICAgcnVuRWZmZWN0cyh0aGlzLCB0aGlzW1RZUEVTLlBST1BBR0FURV0sIGNoYW5nZWRQcm9wcywgb2xkUHJvcHMsIGhhc1BhdGhzKTsKICAgICAgICB9CiAgICAgICAgbGV0IHRlbXBsYXRlSW5mbyA9IHRoaXMuX190ZW1wbGF0ZUluZm87CiAgICAgICAgd2hpbGUgKHRlbXBsYXRlSW5mbykgewogICAgICAgICAgcnVuRWZmZWN0cyh0aGlzLCB0ZW1wbGF0ZUluZm8ucHJvcGVydHlFZmZlY3RzLCBjaGFuZ2VkUHJvcHMsIG9sZFByb3BzLAogICAgICAgICAgICBoYXNQYXRocywgdGVtcGxhdGVJbmZvLm5vZGVMaXN0KTsKICAgICAgICAgIHRlbXBsYXRlSW5mbyA9IHRlbXBsYXRlSW5mby5uZXh0VGVtcGxhdGVJbmZvOwogICAgICAgIH0KICAgICAgfQoKICAgICAgLyoqCiAgICAgICAqIEFsaWFzZXMgb25lIGRhdGEgcGF0aCBhcyBhbm90aGVyLCBzdWNoIHRoYXQgcGF0aCBub3RpZmljYXRpb25zIGZyb20gb25lCiAgICAgICAqIGFyZSByb3V0ZWQgdG8gdGhlIG90aGVyLgogICAgICAgKgogICAgICAgKiBAcGFyYW0ge3N0cmluZyB8ICFBcnJheTxzdHJpbmd8bnVtYmVyPn0gdG8gVGFyZ2V0IHBhdGggdG8gbGluay4KICAgICAgICogQHBhcmFtIHtzdHJpbmcgfCAhQXJyYXk8c3RyaW5nfG51bWJlcj59IGZyb20gU291cmNlIHBhdGggdG8gbGluay4KICAgICAgICogQHJldHVybiB7dm9pZH0KICAgICAgICogQHB1YmxpYwogICAgICAgKi8KICAgICAgbGlua1BhdGhzKHRvLCBmcm9tKSB7CiAgICAgICAgdG8gPSBQb2x5bWVyLlBhdGgubm9ybWFsaXplKHRvKTsKICAgICAgICBmcm9tID0gUG9seW1lci5QYXRoLm5vcm1hbGl6ZShmcm9tKTsKICAgICAgICB0aGlzLl9fZGF0YUxpbmtlZFBhdGhzID0gdGhpcy5fX2RhdGFMaW5rZWRQYXRocyB8fCB7fTsKICAgICAgICB0aGlzLl9fZGF0YUxpbmtlZFBhdGhzW3RvXSA9IGZyb207CiAgICAgIH0KCiAgICAgIC8qKgogICAgICAgKiBSZW1vdmVzIGEgZGF0YSBwYXRoIGFsaWFzIHByZXZpb3VzbHkgZXN0YWJsaXNoZWQgd2l0aCBgX2xpbmtQYXRoc2AuCiAgICAgICAqCiAgICAgICAqIE5vdGUsIHRoZSBwYXRoIHRvIHVubGluayBzaG91bGQgYmUgdGhlIHRhcmdldCAoYHRvYCkgdXNlZCB3aGVuCiAgICAgICAqIGxpbmtpbmcgdGhlIHBhdGhzLgogICAgICAgKgogICAgICAgKiBAcGFyYW0ge3N0cmluZyB8ICFBcnJheTxzdHJpbmd8bnVtYmVyPn0gcGF0aCBUYXJnZXQgcGF0aCB0byB1bmxpbmsuCiAgICAgICAqIEByZXR1cm4ge3ZvaWR9CiAgICAgICAqIEBwdWJsaWMKICAgICAgICovCiAgICAgIHVubGlua1BhdGhzKHBhdGgpIHsKICAgICAgICBwYXRoID0gUG9seW1lci5QYXRoLm5vcm1hbGl6ZShwYXRoKTsKICAgICAgICBpZiAodGhpcy5fX2RhdGFMaW5rZWRQYXRocykgewogICAgICAgICAgZGVsZXRlIHRoaXMuX19kYXRhTGlua2VkUGF0aHNbcGF0aF07CiAgICAgICAgfQogICAgICB9CgogICAgICAvKioKICAgICAgICogTm90aWZ5IHRoYXQgYW4gYXJyYXkgaGFzIGNoYW5nZWQuCiAgICAgICAqCiAgICAgICAqIEV4YW1wbGU6CiAgICAgICAqCiAgICAgICAqICAgICB0aGlzLml0ZW1zID0gWyB7bmFtZTogJ0ppbSd9LCB7bmFtZTogJ1RvZGQnfSwge25hbWU6ICdCaWxsJ30gXTsKICAgICAgICogICAgIC4uLgogICAgICAgKiAgICAgdGhpcy5pdGVtcy5zcGxpY2UoMSwgMSwge25hbWU6ICdTYW0nfSk7CiAgICAgICAqICAgICB0aGlzLml0ZW1zLnB1c2goe25hbWU6ICdCb2InfSk7CiAgICAgICAqICAgICB0aGlzLm5vdGlmeVNwbGljZXMoJ2l0ZW1zJywgWwogICAgICAgKiAgICAgICB7IGluZGV4OiAxLCByZW1vdmVkOiBbe25hbWU6ICdUb2RkJ31dLCBhZGRlZENvdW50OiAxLCBvYmplY3Q6IHRoaXMuaXRlbXMsIHR5cGU6ICdzcGxpY2UnIH0sCiAgICAgICAqICAgICAgIHsgaW5kZXg6IDMsIHJlbW92ZWQ6IFtdLCBhZGRlZENvdW50OiAxLCBvYmplY3Q6IHRoaXMuaXRlbXMsIHR5cGU6ICdzcGxpY2UnfQogICAgICAgKiAgICAgXSk7CiAgICAgICAqCiAgICAgICAqIEBwYXJhbSB7c3RyaW5nfSBwYXRoIFBhdGggdGhhdCBzaG91bGQgYmUgbm90aWZpZWQuCiAgICAgICAqIEBwYXJhbSB7QXJyYXl9IHNwbGljZXMgQXJyYXkgb2Ygc3BsaWNlIHJlY29yZHMgaW5kaWNhdGluZyBvcmRlcmVkCiAgICAgICAqICAgY2hhbmdlcyB0aGF0IG9jY3VycmVkIHRvIHRoZSBhcnJheS4gRWFjaCByZWNvcmQgc2hvdWxkIGhhdmUgdGhlCiAgICAgICAqICAgZm9sbG93aW5nIGZpZWxkczoKICAgICAgICogICAgKiBpbmRleDogaW5kZXggYXQgd2hpY2ggdGhlIGNoYW5nZSBvY2N1cnJlZAogICAgICAgKiAgICAqIHJlbW92ZWQ6IGFycmF5IG9mIGl0ZW1zIHRoYXQgd2VyZSByZW1vdmVkIGZyb20gdGhpcyBpbmRleAogICAgICAgKiAgICAqIGFkZGVkQ291bnQ6IG51bWJlciBvZiBuZXcgaXRlbXMgYWRkZWQgYXQgdGhpcyBpbmRleAogICAgICAgKiAgICAqIG9iamVjdDogYSByZWZlcmVuY2UgdG8gdGhlIGFycmF5IGluIHF1ZXN0aW9uCiAgICAgICAqICAgICogdHlwZTogdGhlIHN0cmluZyBsaXRlcmFsICdzcGxpY2UnCiAgICAgICAqCiAgICAgICAqICAgTm90ZSB0aGF0IHNwbGljZSByZWNvcmRzIF9tdXN0XyBiZSBub3JtYWxpemVkIHN1Y2ggdGhhdCB0aGV5IGFyZQogICAgICAgKiAgIHJlcG9ydGVkIGluIGluZGV4IG9yZGVyIChyYXcgcmVzdWx0cyBmcm9tIGBPYmplY3Qub2JzZXJ2ZWAgYXJlIG5vdAogICAgICAgKiAgIG9yZGVyZWQgYW5kIG11c3QgYmUgbm9ybWFsaXplZC9tZXJnZWQgYmVmb3JlIG5vdGlmeWluZykuCiAgICAgICAqIEByZXR1cm4ge3ZvaWR9CiAgICAgICAqIEBwdWJsaWMKICAgICAgKi8KICAgICAgbm90aWZ5U3BsaWNlcyhwYXRoLCBzcGxpY2VzKSB7CiAgICAgICAgbGV0IGluZm8gPSB7cGF0aDogJyd9OwogICAgICAgIGxldCBhcnJheSA9IC8qKiBAdHlwZSB7QXJyYXl9ICovKFBvbHltZXIuUGF0aC5nZXQodGhpcywgcGF0aCwgaW5mbykpOwogICAgICAgIG5vdGlmeVNwbGljZXModGhpcywgYXJyYXksIGluZm8ucGF0aCwgc3BsaWNlcyk7CiAgICAgIH0KCiAgICAgIC8qKgogICAgICAgKiBDb252ZW5pZW5jZSBtZXRob2QgZm9yIHJlYWRpbmcgYSB2YWx1ZSBmcm9tIGEgcGF0aC4KICAgICAgICoKICAgICAgICogTm90ZSwgaWYgYW55IHBhcnQgaW4gdGhlIHBhdGggaXMgdW5kZWZpbmVkLCB0aGlzIG1ldGhvZCByZXR1cm5zCiAgICAgICAqIGB1bmRlZmluZWRgICh0aGlzIG1ldGhvZCBkb2VzIG5vdCB0aHJvdyB3aGVuIGRlcmVmZXJlbmNpbmcgdW5kZWZpbmVkCiAgICAgICAqIHBhdGhzKS4KICAgICAgICoKICAgICAgICogQHBhcmFtIHsoc3RyaW5nfCFBcnJheTwoc3RyaW5nfG51bWJlcik+KX0gcGF0aCBQYXRoIHRvIHRoZSB2YWx1ZQogICAgICAgKiAgIHRvIHJlYWQuICBUaGUgcGF0aCBtYXkgYmUgc3BlY2lmaWVkIGFzIGEgc3RyaW5nIChlLmcuIGBmb28uYmFyLmJhemApCiAgICAgICAqICAgb3IgYW4gYXJyYXkgb2YgcGF0aCBwYXJ0cyAoZS5nLiBgWydmb28uYmFyJywgJ2JheiddYCkuICBOb3RlIHRoYXQKICAgICAgICogICBicmFja2V0ZWQgZXhwcmVzc2lvbnMgYXJlIG5vdCBzdXBwb3J0ZWQ7IHN0cmluZy1iYXNlZCBwYXRoIHBhcnRzCiAgICAgICAqICAgKm11c3QqIGJlIHNlcGFyYXRlZCBieSBkb3RzLiAgTm90ZSB0aGF0IHdoZW4gZGVyZWZlcmVuY2luZyBhcnJheQogICAgICAgKiAgIGluZGljZXMsIHRoZSBpbmRleCBtYXkgYmUgdXNlZCBhcyBhIGRvdHRlZCBwYXJ0IGRpcmVjdGx5CiAgICAgICAqICAgKGUuZy4gYHVzZXJzLjEyLm5hbWVgIG9yIGBbJ3VzZXJzJywgMTIsICduYW1lJ11gKS4KICAgICAgICogQHBhcmFtIHtPYmplY3Q9fSByb290IFJvb3Qgb2JqZWN0IGZyb20gd2hpY2ggdGhlIHBhdGggaXMgZXZhbHVhdGVkLgogICAgICAgKiBAcmV0dXJuIHsqfSBWYWx1ZSBhdCB0aGUgcGF0aCwgb3IgYHVuZGVmaW5lZGAgaWYgYW55IHBhcnQgb2YgdGhlIHBhdGgKICAgICAgICogICBpcyB1bmRlZmluZWQuCiAgICAgICAqIEBwdWJsaWMKICAgICAgICovCiAgICAgIGdldChwYXRoLCByb290KSB7CiAgICAgICAgcmV0dXJuIFBvbHltZXIuUGF0aC5nZXQocm9vdCB8fCB0aGlzLCBwYXRoKTsKICAgICAgfQoKICAgICAgLyoqCiAgICAgICAqIENvbnZlbmllbmNlIG1ldGhvZCBmb3Igc2V0dGluZyBhIHZhbHVlIHRvIGEgcGF0aCBhbmQgbm90aWZ5aW5nIGFueQogICAgICAgKiBlbGVtZW50cyBib3VuZCB0byB0aGUgc2FtZSBwYXRoLgogICAgICAgKgogICAgICAgKiBOb3RlLCBpZiBhbnkgcGFydCBpbiB0aGUgcGF0aCBleGNlcHQgZm9yIHRoZSBsYXN0IGlzIHVuZGVmaW5lZCwKICAgICAgICogdGhpcyBtZXRob2QgZG9lcyBub3RoaW5nICh0aGlzIG1ldGhvZCBkb2VzIG5vdCB0aHJvdyB3aGVuCiAgICAgICAqIGRlcmVmZXJlbmNpbmcgdW5kZWZpbmVkIHBhdGhzKS4KICAgICAgICoKICAgICAgICogQHBhcmFtIHsoc3RyaW5nfCFBcnJheTwoc3RyaW5nfG51bWJlcik+KX0gcGF0aCBQYXRoIHRvIHRoZSB2YWx1ZQogICAgICAgKiAgIHRvIHdyaXRlLiAgVGhlIHBhdGggbWF5IGJlIHNwZWNpZmllZCBhcyBhIHN0cmluZyAoZS5nLiBgJ2Zvby5iYXIuYmF6J2ApCiAgICAgICAqICAgb3IgYW4gYXJyYXkgb2YgcGF0aCBwYXJ0cyAoZS5nLiBgWydmb28uYmFyJywgJ2JheiddYCkuICBOb3RlIHRoYXQKICAgICAgICogICBicmFja2V0ZWQgZXhwcmVzc2lvbnMgYXJlIG5vdCBzdXBwb3J0ZWQ7IHN0cmluZy1iYXNlZCBwYXRoIHBhcnRzCiAgICAgICAqICAgKm11c3QqIGJlIHNlcGFyYXRlZCBieSBkb3RzLiAgTm90ZSB0aGF0IHdoZW4gZGVyZWZlcmVuY2luZyBhcnJheQogICAgICAgKiAgIGluZGljZXMsIHRoZSBpbmRleCBtYXkgYmUgdXNlZCBhcyBhIGRvdHRlZCBwYXJ0IGRpcmVjdGx5CiAgICAgICAqICAgKGUuZy4gYCd1c2Vycy4xMi5uYW1lJ2Agb3IgYFsndXNlcnMnLCAxMiwgJ25hbWUnXWApLgogICAgICAgKiBAcGFyYW0geyp9IHZhbHVlIFZhbHVlIHRvIHNldCBhdCB0aGUgc3BlY2lmaWVkIHBhdGguCiAgICAgICAqIEBwYXJhbSB7T2JqZWN0PX0gcm9vdCBSb290IG9iamVjdCBmcm9tIHdoaWNoIHRoZSBwYXRoIGlzIGV2YWx1YXRlZC4KICAgICAgICogICBXaGVuIHNwZWNpZmllZCwgbm8gbm90aWZpY2F0aW9uIHdpbGwgb2NjdXIuCiAgICAgICAqIEByZXR1cm4ge3ZvaWR9CiAgICAgICAqIEBwdWJsaWMKICAgICAgKi8KICAgICAgc2V0KHBhdGgsIHZhbHVlLCByb290KSB7CiAgICAgICAgaWYgKHJvb3QpIHsKICAgICAgICAgIFBvbHltZXIuUGF0aC5zZXQocm9vdCwgcGF0aCwgdmFsdWUpOwogICAgICAgIH0gZWxzZSB7CiAgICAgICAgICBpZiAoIXRoaXNbVFlQRVMuUkVBRF9PTkxZXSB8fCAhdGhpc1tUWVBFUy5SRUFEX09OTFldWy8qKiBAdHlwZSB7c3RyaW5nfSAqLyhwYXRoKV0pIHsKICAgICAgICAgICAgaWYgKHRoaXMuX3NldFBlbmRpbmdQcm9wZXJ0eU9yUGF0aChwYXRoLCB2YWx1ZSwgdHJ1ZSkpIHsKICAgICAgICAgICAgICB0aGlzLl9pbnZhbGlkYXRlUHJvcGVydGllcygpOwogICAgICAgICAgICB9CiAgICAgICAgICB9CiAgICAgICAgfQogICAgICB9CgogICAgICAvKioKICAgICAgICogQWRkcyBpdGVtcyBvbnRvIHRoZSBlbmQgb2YgdGhlIGFycmF5IGF0IHRoZSBwYXRoIHNwZWNpZmllZC4KICAgICAgICoKICAgICAgICogVGhlIGFyZ3VtZW50cyBhZnRlciBgcGF0aGAgYW5kIHJldHVybiB2YWx1ZSBtYXRjaCB0aGF0IG9mCiAgICAgICAqIGBBcnJheS5wcm90b3R5cGUucHVzaGAuCiAgICAgICAqCiAgICAgICAqIFRoaXMgbWV0aG9kIG5vdGlmaWVzIG90aGVyIHBhdGhzIHRvIHRoZSBzYW1lIGFycmF5IHRoYXQgYQogICAgICAgKiBzcGxpY2Ugb2NjdXJyZWQgdG8gdGhlIGFycmF5LgogICAgICAgKgogICAgICAgKiBAcGFyYW0ge3N0cmluZyB8ICFBcnJheTxzdHJpbmd8bnVtYmVyPn0gcGF0aCBQYXRoIHRvIGFycmF5LgogICAgICAgKiBAcGFyYW0gey4uLip9IGl0ZW1zIEl0ZW1zIHRvIHB1c2ggb250byBhcnJheQogICAgICAgKiBAcmV0dXJuIHtudW1iZXJ9IE5ldyBsZW5ndGggb2YgdGhlIGFycmF5LgogICAgICAgKiBAcHVibGljCiAgICAgICAqLwogICAgICBwdXNoKHBhdGgsIC4uLml0ZW1zKSB7CiAgICAgICAgbGV0IGluZm8gPSB7cGF0aDogJyd9OwogICAgICAgIGxldCBhcnJheSA9IC8qKiBAdHlwZSB7QXJyYXl9Ki8oUG9seW1lci5QYXRoLmdldCh0aGlzLCBwYXRoLCBpbmZvKSk7CiAgICAgICAgbGV0IGxlbiA9IGFycmF5Lmxlbmd0aDsKICAgICAgICBsZXQgcmV0ID0gYXJyYXkucHVzaCguLi5pdGVtcyk7CiAgICAgICAgaWYgKGl0ZW1zLmxlbmd0aCkgewogICAgICAgICAgbm90aWZ5U3BsaWNlKHRoaXMsIGFycmF5LCBpbmZvLnBhdGgsIGxlbiwgaXRlbXMubGVuZ3RoLCBbXSk7CiAgICAgICAgfQogICAgICAgIHJldHVybiByZXQ7CiAgICAgIH0KCiAgICAgIC8qKgogICAgICAgKiBSZW1vdmVzIGFuIGl0ZW0gZnJvbSB0aGUgZW5kIG9mIGFycmF5IGF0IHRoZSBwYXRoIHNwZWNpZmllZC4KICAgICAgICoKICAgICAgICogVGhlIGFyZ3VtZW50cyBhZnRlciBgcGF0aGAgYW5kIHJldHVybiB2YWx1ZSBtYXRjaCB0aGF0IG9mCiAgICAgICAqIGBBcnJheS5wcm90b3R5cGUucG9wYC4KICAgICAgICoKICAgICAgICogVGhpcyBtZXRob2Qgbm90aWZpZXMgb3RoZXIgcGF0aHMgdG8gdGhlIHNhbWUgYXJyYXkgdGhhdCBhCiAgICAgICAqIHNwbGljZSBvY2N1cnJlZCB0byB0aGUgYXJyYXkuCiAgICAgICAqCiAgICAgICAqIEBwYXJhbSB7c3RyaW5nIHwgIUFycmF5PHN0cmluZ3xudW1iZXI+fSBwYXRoIFBhdGggdG8gYXJyYXkuCiAgICAgICAqIEByZXR1cm4geyp9IEl0ZW0gdGhhdCB3YXMgcmVtb3ZlZC4KICAgICAgICogQHB1YmxpYwogICAgICAgKi8KICAgICAgcG9wKHBhdGgpIHsKICAgICAgICBsZXQgaW5mbyA9IHtwYXRoOiAnJ307CiAgICAgICAgbGV0IGFycmF5ID0gLyoqIEB0eXBlIHtBcnJheX0gKi8oUG9seW1lci5QYXRoLmdldCh0aGlzLCBwYXRoLCBpbmZvKSk7CiAgICAgICAgbGV0IGhhZExlbmd0aCA9IEJvb2xlYW4oYXJyYXkubGVuZ3RoKTsKICAgICAgICBsZXQgcmV0ID0gYXJyYXkucG9wKCk7CiAgICAgICAgaWYgKGhhZExlbmd0aCkgewogICAgICAgICAgbm90aWZ5U3BsaWNlKHRoaXMsIGFycmF5LCBpbmZvLnBhdGgsIGFycmF5Lmxlbmd0aCwgMCwgW3JldF0pOwogICAgICAgIH0KICAgICAgICByZXR1cm4gcmV0OwogICAgICB9CgogICAgICAvKioKICAgICAgICogU3RhcnRpbmcgZnJvbSB0aGUgc3RhcnQgaW5kZXggc3BlY2lmaWVkLCByZW1vdmVzIDAgb3IgbW9yZSBpdGVtcwogICAgICAgKiBmcm9tIHRoZSBhcnJheSBhbmQgaW5zZXJ0cyAwIG9yIG1vcmUgbmV3IGl0ZW1zIGluIHRoZWlyIHBsYWNlLgogICAgICAgKgogICAgICAgKiBUaGUgYXJndW1lbnRzIGFmdGVyIGBwYXRoYCBhbmQgcmV0dXJuIHZhbHVlIG1hdGNoIHRoYXQgb2YKICAgICAgICogYEFycmF5LnByb3RvdHlwZS5zcGxpY2VgLgogICAgICAgKgogICAgICAgKiBUaGlzIG1ldGhvZCBub3RpZmllcyBvdGhlciBwYXRocyB0byB0aGUgc2FtZSBhcnJheSB0aGF0IGEKICAgICAgICogc3BsaWNlIG9jY3VycmVkIHRvIHRoZSBhcnJheS4KICAgICAgICoKICAgICAgICogQHBhcmFtIHtzdHJpbmcgfCAhQXJyYXk8c3RyaW5nfG51bWJlcj59IHBhdGggUGF0aCB0byBhcnJheS4KICAgICAgICogQHBhcmFtIHtudW1iZXJ9IHN0YXJ0IEluZGV4IGZyb20gd2hpY2ggdG8gc3RhcnQgcmVtb3ZpbmcvaW5zZXJ0aW5nLgogICAgICAgKiBAcGFyYW0ge251bWJlcn0gZGVsZXRlQ291bnQgTnVtYmVyIG9mIGl0ZW1zIHRvIHJlbW92ZS4KICAgICAgICogQHBhcmFtIHsuLi4qfSBpdGVtcyBJdGVtcyB0byBpbnNlcnQgaW50byBhcnJheS4KICAgICAgICogQHJldHVybiB7QXJyYXl9IEFycmF5IG9mIHJlbW92ZWQgaXRlbXMuCiAgICAgICAqIEBwdWJsaWMKICAgICAgICovCiAgICAgIHNwbGljZShwYXRoLCBzdGFydCwgZGVsZXRlQ291bnQsIC4uLml0ZW1zKSB7CiAgICAgICAgbGV0IGluZm8gPSB7cGF0aCA6ICcnfTsKICAgICAgICBsZXQgYXJyYXkgPSAvKiogQHR5cGUge0FycmF5fSAqLyhQb2x5bWVyLlBhdGguZ2V0KHRoaXMsIHBhdGgsIGluZm8pKTsKICAgICAgICAvLyBOb3JtYWxpemUgZmFuY3kgbmF0aXZlIHNwbGljZSBoYW5kbGluZyBvZiBjcmF6eSBzdGFydCB2YWx1ZXMKICAgICAgICBpZiAoc3RhcnQgPCAwKSB7CiAgICAgICAgICBzdGFydCA9IGFycmF5Lmxlbmd0aCAtIE1hdGguZmxvb3IoLXN0YXJ0KTsKICAgICAgICB9IGVsc2UgaWYgKHN0YXJ0KSB7CiAgICAgICAgICBzdGFydCA9IE1hdGguZmxvb3Ioc3RhcnQpOwogICAgICAgIH0KICAgICAgICAvLyBhcnJheS5zcGxpY2UgZG9lcyBkaWZmZXJlbnQgdGhpbmdzIGJhc2VkIG9uIHRoZSBudW1iZXIgb2YgYXJndW1lbnRzCiAgICAgICAgLy8geW91IHBhc3MgaW4uIFRoZXJlZm9yZSwgYXJyYXkuc3BsaWNlKDApIGFuZCBhcnJheS5zcGxpY2UoMCwgdW5kZWZpbmVkKQogICAgICAgIC8vIGRvIGRpZmZlcmVudCB0aGluZ3MuIEluIHRoZSBmb3JtZXIsIHRoZSB3aG9sZSBhcnJheSBpcyBjbGVhcmVkLiBJbiB0aGUKICAgICAgICAvLyBsYXR0ZXIsIG5vIGl0ZW1zIGFyZSByZW1vdmVkLgogICAgICAgIC8vIFRoaXMgbWVhbnMgdGhhdCB3ZSBuZWVkIHRvIGRldGVjdCB3aGV0aGVyIDEuIG9uZSBvZiB0aGUgYXJndW1lbnRzCiAgICAgICAgLy8gaXMgYWN0dWFsbHkgcGFzc2VkIGluIGFuZCB0aGVuIDIuIGRldGVybWluZSBob3cgbWFueSBhcmd1bWVudHMKICAgICAgICAvLyB3ZSBzaG91bGQgcGFzcyBvbiB0byB0aGUgbmF0aXZlIGFycmF5LnNwbGljZQogICAgICAgIC8vCiAgICAgICAgbGV0IHJldDsKICAgICAgICAvLyBPbWl0IGFueSBhZGRpdGlvbmFsIGFyZ3VtZW50cyBpZiB0aGV5IHdlcmUgbm90IHBhc3NlZCBpbgogICAgICAgIGlmIChhcmd1bWVudHMubGVuZ3RoID09PSAyKSB7CiAgICAgICAgICByZXQgPSBhcnJheS5zcGxpY2Uoc3RhcnQpOwogICAgICAgIC8vIEVpdGhlciBzdGFydCB3YXMgdW5kZWZpbmVkIGFuZCB0aGUgb3RoZXJzIHdlcmUgZGVmaW5lZCwgYnV0IGluIHRoaXMKICAgICAgICAvLyBjYXNlIHdlIGNhbiBzYWZlbHkgcGFzcyBvbiBhbGwgYXJndW1lbnRzCiAgICAgICAgLy8KICAgICAgICAvLyBOb3RlOiB0aGlzIGluY2x1ZGVzIHRoZSBjYXNlIHdoZXJlIG5vbmUgb2YgdGhlIGFyZ3VtZW50cyB3ZXJlIHBhc3NlZCBpbiwKICAgICAgICAvLyBlLmcuIHRoaXMuc3BsaWNlKCdhcnJheScpLiBIb3dldmVyLCBpZiBib3RoIHN0YXJ0IGFuZCBkZWxldGVDb3VudAogICAgICAgIC8vIGFyZSB1bmRlZmluZWQsIGFycmF5LnNwbGljZSB3aWxsIG5vdCBtb2RpZnkgdGhlIGFycmF5IChhcyBleHBlY3RlZCkKICAgICAgICB9IGVsc2UgewogICAgICAgICAgcmV0ID0gYXJyYXkuc3BsaWNlKHN0YXJ0LCBkZWxldGVDb3VudCwgLi4uaXRlbXMpOwogICAgICAgIH0KICAgICAgICAvLyBBdCB0aGUgZW5kLCBjaGVjayB3aGV0aGVyIGFueSBpdGVtcyB3ZXJlIHBhc3NlZCBpbiAoZS5nLiBpbnNlcnRpb25zKQogICAgICAgIC8vIG9yIGlmIHRoZSByZXR1cm4gYXJyYXkgY29udGFpbnMgaXRlbXMgKGUuZy4gZGVsZXRpb25zKS4KICAgICAgICAvLyBPbmx5IG5vdGlmeSBpZiBpdGVtcyB3ZXJlIGFkZGVkIG9yIGRlbGV0ZWQuCiAgICAgICAgaWYgKGl0ZW1zLmxlbmd0aCB8fCByZXQubGVuZ3RoKSB7CiAgICAgICAgICBub3RpZnlTcGxpY2UodGhpcywgYXJyYXksIGluZm8ucGF0aCwgc3RhcnQsIGl0ZW1zLmxlbmd0aCwgcmV0KTsKICAgICAgICB9CiAgICAgICAgcmV0dXJuIHJldDsKICAgICAgfQoKICAgICAgLyoqCiAgICAgICAqIFJlbW92ZXMgYW4gaXRlbSBmcm9tIHRoZSBiZWdpbm5pbmcgb2YgYXJyYXkgYXQgdGhlIHBhdGggc3BlY2lmaWVkLgogICAgICAgKgogICAgICAgKiBUaGUgYXJndW1lbnRzIGFmdGVyIGBwYXRoYCBhbmQgcmV0dXJuIHZhbHVlIG1hdGNoIHRoYXQgb2YKICAgICAgICogYEFycmF5LnByb3RvdHlwZS5wb3BgLgogICAgICAgKgogICAgICAgKiBUaGlzIG1ldGhvZCBub3RpZmllcyBvdGhlciBwYXRocyB0byB0aGUgc2FtZSBhcnJheSB0aGF0IGEKICAgICAgICogc3BsaWNlIG9jY3VycmVkIHRvIHRoZSBhcnJheS4KICAgICAgICoKICAgICAgICogQHBhcmFtIHtzdHJpbmcgfCAhQXJyYXk8c3RyaW5nfG51bWJlcj59IHBhdGggUGF0aCB0byBhcnJheS4KICAgICAgICogQHJldHVybiB7Kn0gSXRlbSB0aGF0IHdhcyByZW1vdmVkLgogICAgICAgKiBAcHVibGljCiAgICAgICAqLwogICAgICBzaGlmdChwYXRoKSB7CiAgICAgICAgbGV0IGluZm8gPSB7cGF0aDogJyd9OwogICAgICAgIGxldCBhcnJheSA9IC8qKiBAdHlwZSB7QXJyYXl9ICovKFBvbHltZXIuUGF0aC5nZXQodGhpcywgcGF0aCwgaW5mbykpOwogICAgICAgIGxldCBoYWRMZW5ndGggPSBCb29sZWFuKGFycmF5Lmxlbmd0aCk7CiAgICAgICAgbGV0IHJldCA9IGFycmF5LnNoaWZ0KCk7CiAgICAgICAgaWYgKGhhZExlbmd0aCkgewogICAgICAgICAgbm90aWZ5U3BsaWNlKHRoaXMsIGFycmF5LCBpbmZvLnBhdGgsIDAsIDAsIFtyZXRdKTsKICAgICAgICB9CiAgICAgICAgcmV0dXJuIHJldDsKICAgICAgfQoKICAgICAgLyoqCiAgICAgICAqIEFkZHMgaXRlbXMgb250byB0aGUgYmVnaW5uaW5nIG9mIHRoZSBhcnJheSBhdCB0aGUgcGF0aCBzcGVjaWZpZWQuCiAgICAgICAqCiAgICAgICAqIFRoZSBhcmd1bWVudHMgYWZ0ZXIgYHBhdGhgIGFuZCByZXR1cm4gdmFsdWUgbWF0Y2ggdGhhdCBvZgogICAgICAgKiBgQXJyYXkucHJvdG90eXBlLnB1c2hgLgogICAgICAgKgogICAgICAgKiBUaGlzIG1ldGhvZCBub3RpZmllcyBvdGhlciBwYXRocyB0byB0aGUgc2FtZSBhcnJheSB0aGF0IGEKICAgICAgICogc3BsaWNlIG9jY3VycmVkIHRvIHRoZSBhcnJheS4KICAgICAgICoKICAgICAgICogQHBhcmFtIHtzdHJpbmcgfCAhQXJyYXk8c3RyaW5nfG51bWJlcj59IHBhdGggUGF0aCB0byBhcnJheS4KICAgICAgICogQHBhcmFtIHsuLi4qfSBpdGVtcyBJdGVtcyB0byBpbnNlcnQgaW5mbyBhcnJheQogICAgICAgKiBAcmV0dXJuIHtudW1iZXJ9IE5ldyBsZW5ndGggb2YgdGhlIGFycmF5LgogICAgICAgKiBAcHVibGljCiAgICAgICAqLwogICAgICB1bnNoaWZ0KHBhdGgsIC4uLml0ZW1zKSB7CiAgICAgICAgbGV0IGluZm8gPSB7cGF0aDogJyd9OwogICAgICAgIGxldCBhcnJheSA9IC8qKiBAdHlwZSB7QXJyYXl9ICovKFBvbHltZXIuUGF0aC5nZXQodGhpcywgcGF0aCwgaW5mbykpOwogICAgICAgIGxldCByZXQgPSBhcnJheS51bnNoaWZ0KC4uLml0ZW1zKTsKICAgICAgICBpZiAoaXRlbXMubGVuZ3RoKSB7CiAgICAgICAgICBub3RpZnlTcGxpY2UodGhpcywgYXJyYXksIGluZm8ucGF0aCwgMCwgaXRlbXMubGVuZ3RoLCBbXSk7CiAgICAgICAgfQogICAgICAgIHJldHVybiByZXQ7CiAgICAgIH0KCiAgICAgIC8qKgogICAgICAgKiBOb3RpZnkgdGhhdCBhIHBhdGggaGFzIGNoYW5nZWQuCiAgICAgICAqCiAgICAgICAqIEV4YW1wbGU6CiAgICAgICAqCiAgICAgICAqICAgICB0aGlzLml0ZW0udXNlci5uYW1lID0gJ0JvYic7CiAgICAgICAqICAgICB0aGlzLm5vdGlmeVBhdGgoJ2l0ZW0udXNlci5uYW1lJyk7CiAgICAgICAqCiAgICAgICAqIEBwYXJhbSB7c3RyaW5nfSBwYXRoIFBhdGggdGhhdCBzaG91bGQgYmUgbm90aWZpZWQuCiAgICAgICAqIEBwYXJhbSB7Kj19IHZhbHVlIFZhbHVlIGF0IHRoZSBwYXRoIChvcHRpb25hbCkuCiAgICAgICAqIEByZXR1cm4ge3ZvaWR9CiAgICAgICAqIEBwdWJsaWMKICAgICAgKi8KICAgICAgbm90aWZ5UGF0aChwYXRoLCB2YWx1ZSkgewogICAgICAgIC8qKiBAdHlwZSB7c3RyaW5nfSAqLwogICAgICAgIGxldCBwcm9wUGF0aDsKICAgICAgICBpZiAoYXJndW1lbnRzLmxlbmd0aCA9PSAxKSB7CiAgICAgICAgICAvLyBHZXQgdmFsdWUgaWYgbm90IHN1cHBsaWVkCiAgICAgICAgICBsZXQgaW5mbyA9IHtwYXRoOiAnJ307CiAgICAgICAgICB2YWx1ZSA9IFBvbHltZXIuUGF0aC5nZXQodGhpcywgcGF0aCwgaW5mbyk7CiAgICAgICAgICBwcm9wUGF0aCA9IGluZm8ucGF0aDsKICAgICAgICB9IGVsc2UgaWYgKEFycmF5LmlzQXJyYXkocGF0aCkpIHsKICAgICAgICAgIC8vIE5vcm1hbGl6ZSBwYXRoIGlmIG5lZWRlZAogICAgICAgICAgcHJvcFBhdGggPSBQb2x5bWVyLlBhdGgubm9ybWFsaXplKHBhdGgpOwogICAgICAgIH0gZWxzZSB7CiAgICAgICAgICBwcm9wUGF0aCA9IC8qKiBAdHlwZXtzdHJpbmd9ICovKHBhdGgpOwogICAgICAgIH0KICAgICAgICBpZiAodGhpcy5fc2V0UGVuZGluZ1Byb3BlcnR5T3JQYXRoKHByb3BQYXRoLCB2YWx1ZSwgdHJ1ZSwgdHJ1ZSkpIHsKICAgICAgICAgIHRoaXMuX2ludmFsaWRhdGVQcm9wZXJ0aWVzKCk7CiAgICAgICAgfQogICAgICB9CgogICAgICAvKioKICAgICAgICogRXF1aXZhbGVudCB0byBzdGF0aWMgYGNyZWF0ZVJlYWRPbmx5UHJvcGVydHlgIEFQSSBidXQgY2FuIGJlIGNhbGxlZCBvbgogICAgICAgKiBhbiBpbnN0YW5jZSB0byBhZGQgZWZmZWN0cyBhdCBydW50aW1lLiAgU2VlIHRoYXQgbWV0aG9kIGZvcgogICAgICAgKiBmdWxsIEFQSSBkb2NzLgogICAgICAgKgogICAgICAgKiBAcGFyYW0ge3N0cmluZ30gcHJvcGVydHkgUHJvcGVydHkgbmFtZQogICAgICAgKiBAcGFyYW0ge2Jvb2xlYW49fSBwcm90ZWN0ZWRTZXR0ZXIgQ3JlYXRlcyBhIGN1c3RvbSBwcm90ZWN0ZWQgc2V0dGVyCiAgICAgICAqICAgd2hlbiBgdHJ1ZWAuCiAgICAgICAqIEByZXR1cm4ge3ZvaWR9CiAgICAgICAqIEBwcm90ZWN0ZWQKICAgICAgICovCiAgICAgIF9jcmVhdGVSZWFkT25seVByb3BlcnR5KHByb3BlcnR5LCBwcm90ZWN0ZWRTZXR0ZXIpIHsKICAgICAgICB0aGlzLl9hZGRQcm9wZXJ0eUVmZmVjdChwcm9wZXJ0eSwgVFlQRVMuUkVBRF9PTkxZKTsKICAgICAgICBpZiAocHJvdGVjdGVkU2V0dGVyKSB7CiAgICAgICAgICB0aGlzWydfc2V0JyArIHVwcGVyKHByb3BlcnR5KV0gPSAvKiogQHRoaXMge1Byb3BlcnR5RWZmZWN0c30gKi9mdW5jdGlvbih2YWx1ZSkgewogICAgICAgICAgICB0aGlzLl9zZXRQcm9wZXJ0eShwcm9wZXJ0eSwgdmFsdWUpOwogICAgICAgICAgfTsKICAgICAgICB9CiAgICAgIH0KCiAgICAgIC8qKgogICAgICAgKiBFcXVpdmFsZW50IHRvIHN0YXRpYyBgY3JlYXRlUHJvcGVydHlPYnNlcnZlcmAgQVBJIGJ1dCBjYW4gYmUgY2FsbGVkIG9uCiAgICAgICAqIGFuIGluc3RhbmNlIHRvIGFkZCBlZmZlY3RzIGF0IHJ1bnRpbWUuICBTZWUgdGhhdCBtZXRob2QgZm9yCiAgICAgICAqIGZ1bGwgQVBJIGRvY3MuCiAgICAgICAqCiAgICAgICAqIEBwYXJhbSB7c3RyaW5nfSBwcm9wZXJ0eSBQcm9wZXJ0eSBuYW1lCiAgICAgICAqIEBwYXJhbSB7c3RyaW5nfGZ1bmN0aW9uKCosKil9IG1ldGhvZCBGdW5jdGlvbiBvciBuYW1lIG9mIG9ic2VydmVyIG1ldGhvZCB0byBjYWxsCiAgICAgICAqIEBwYXJhbSB7Ym9vbGVhbj19IGR5bmFtaWNGbiBXaGV0aGVyIHRoZSBtZXRob2QgbmFtZSBzaG91bGQgYmUgaW5jbHVkZWQgYXMKICAgICAgICogICBhIGRlcGVuZGVuY3kgdG8gdGhlIGVmZmVjdC4KICAgICAgICogQHJldHVybiB7dm9pZH0KICAgICAgICogQHByb3RlY3RlZAogICAgICAgKi8KICAgICAgX2NyZWF0ZVByb3BlcnR5T2JzZXJ2ZXIocHJvcGVydHksIG1ldGhvZCwgZHluYW1pY0ZuKSB7CiAgICAgICAgbGV0IGluZm8gPSB7IHByb3BlcnR5LCBtZXRob2QsIGR5bmFtaWNGbjogQm9vbGVhbihkeW5hbWljRm4pIH07CiAgICAgICAgdGhpcy5fYWRkUHJvcGVydHlFZmZlY3QocHJvcGVydHksIFRZUEVTLk9CU0VSVkUsIHsKICAgICAgICAgIGZuOiBydW5PYnNlcnZlckVmZmVjdCwgaW5mbywgdHJpZ2dlcjoge25hbWU6IHByb3BlcnR5fQogICAgICAgIH0pOwogICAgICAgIGlmIChkeW5hbWljRm4pIHsKICAgICAgICAgIHRoaXMuX2FkZFByb3BlcnR5RWZmZWN0KC8qKiBAdHlwZSB7c3RyaW5nfSAqLyhtZXRob2QpLCBUWVBFUy5PQlNFUlZFLCB7CiAgICAgICAgICAgIGZuOiBydW5PYnNlcnZlckVmZmVjdCwgaW5mbywgdHJpZ2dlcjoge25hbWU6IG1ldGhvZH0KICAgICAgICAgIH0pOwogICAgICAgIH0KICAgICAgfQoKICAgICAgLyoqCiAgICAgICAqIEVxdWl2YWxlbnQgdG8gc3RhdGljIGBjcmVhdGVNZXRob2RPYnNlcnZlcmAgQVBJIGJ1dCBjYW4gYmUgY2FsbGVkIG9uCiAgICAgICAqIGFuIGluc3RhbmNlIHRvIGFkZCBlZmZlY3RzIGF0IHJ1bnRpbWUuICBTZWUgdGhhdCBtZXRob2QgZm9yCiAgICAgICAqIGZ1bGwgQVBJIGRvY3MuCiAgICAgICAqCiAgICAgICAqIEBwYXJhbSB7c3RyaW5nfSBleHByZXNzaW9uIE1ldGhvZCBleHByZXNzaW9uCiAgICAgICAqIEBwYXJhbSB7Ym9vbGVhbnxPYmplY3Q9fSBkeW5hbWljRm4gQm9vbGVhbiBvciBvYmplY3QgbWFwIGluZGljYXRpbmcKICAgICAgICogICB3aGV0aGVyIG1ldGhvZCBuYW1lcyBzaG91bGQgYmUgaW5jbHVkZWQgYXMgYSBkZXBlbmRlbmN5IHRvIHRoZSBlZmZlY3QuCiAgICAgICAqIEByZXR1cm4ge3ZvaWR9CiAgICAgICAqIEBwcm90ZWN0ZWQKICAgICAgICovCiAgICAgIF9jcmVhdGVNZXRob2RPYnNlcnZlcihleHByZXNzaW9uLCBkeW5hbWljRm4pIHsKICAgICAgICBsZXQgc2lnID0gcGFyc2VNZXRob2QoZXhwcmVzc2lvbik7CiAgICAgICAgaWYgKCFzaWcpIHsKICAgICAgICAgIHRocm93IG5ldyBFcnJvcigiTWFsZm9ybWVkIG9ic2VydmVyIGV4cHJlc3Npb24gJyIgKyBleHByZXNzaW9uICsgIiciKTsKICAgICAgICB9CiAgICAgICAgY3JlYXRlTWV0aG9kRWZmZWN0KHRoaXMsIHNpZywgVFlQRVMuT0JTRVJWRSwgcnVuTWV0aG9kRWZmZWN0LCBudWxsLCBkeW5hbWljRm4pOwogICAgICB9CgogICAgICAvKioKICAgICAgICogRXF1aXZhbGVudCB0byBzdGF0aWMgYGNyZWF0ZU5vdGlmeWluZ1Byb3BlcnR5YCBBUEkgYnV0IGNhbiBiZSBjYWxsZWQgb24KICAgICAgICogYW4gaW5zdGFuY2UgdG8gYWRkIGVmZmVjdHMgYXQgcnVudGltZS4gIFNlZSB0aGF0IG1ldGhvZCBmb3IKICAgICAgICogZnVsbCBBUEkgZG9jcy4KICAgICAgICoKICAgICAgICogQHBhcmFtIHtzdHJpbmd9IHByb3BlcnR5IFByb3BlcnR5IG5hbWUKICAgICAgICogQHJldHVybiB7dm9pZH0KICAgICAgICogQHByb3RlY3RlZAogICAgICAgKi8KICAgICAgX2NyZWF0ZU5vdGlmeWluZ1Byb3BlcnR5KHByb3BlcnR5KSB7CiAgICAgICAgdGhpcy5fYWRkUHJvcGVydHlFZmZlY3QocHJvcGVydHksIFRZUEVTLk5PVElGWSwgewogICAgICAgICAgZm46IHJ1bk5vdGlmeUVmZmVjdCwKICAgICAgICAgIGluZm86IHsKICAgICAgICAgICAgZXZlbnROYW1lOiBDYXNlTWFwLmNhbWVsVG9EYXNoQ2FzZShwcm9wZXJ0eSkgKyAnLWNoYW5nZWQnLAogICAgICAgICAgICBwcm9wZXJ0eTogcHJvcGVydHkKICAgICAgICAgIH0KICAgICAgICB9KTsKICAgICAgfQoKICAgICAgLyoqCiAgICAgICAqIEVxdWl2YWxlbnQgdG8gc3RhdGljIGBjcmVhdGVSZWZsZWN0ZWRQcm9wZXJ0eWAgQVBJIGJ1dCBjYW4gYmUgY2FsbGVkIG9uCiAgICAgICAqIGFuIGluc3RhbmNlIHRvIGFkZCBlZmZlY3RzIGF0IHJ1bnRpbWUuICBTZWUgdGhhdCBtZXRob2QgZm9yCiAgICAgICAqIGZ1bGwgQVBJIGRvY3MuCiAgICAgICAqCiAgICAgICAqIEBwYXJhbSB7c3RyaW5nfSBwcm9wZXJ0eSBQcm9wZXJ0eSBuYW1lCiAgICAgICAqIEByZXR1cm4ge3ZvaWR9CiAgICAgICAqIEBwcm90ZWN0ZWQKICAgICAgICovCiAgICAgIF9jcmVhdGVSZWZsZWN0ZWRQcm9wZXJ0eShwcm9wZXJ0eSkgewogICAgICAgIGxldCBhdHRyID0gdGhpcy5jb25zdHJ1Y3Rvci5hdHRyaWJ1dGVOYW1lRm9yUHJvcGVydHkocHJvcGVydHkpOwogICAgICAgIGlmIChhdHRyWzBdID09PSAnLScpIHsKICAgICAgICAgIGNvbnNvbGUud2FybignUHJvcGVydHkgJyArIHByb3BlcnR5ICsgJyBjYW5ub3QgYmUgcmVmbGVjdGVkIHRvIGF0dHJpYnV0ZSAnICsKICAgICAgICAgICAgYXR0ciArICcgYmVjYXVzZSAiLSIgaXMgbm90IGEgdmFsaWQgc3RhcnRpbmcgYXR0cmlidXRlIG5hbWUuIFVzZSBhIGxvd2VyY2FzZSBmaXJzdCBsZXR0ZXIgZm9yIHRoZSBwcm9wZXJ0eSBpbnN0ZWFkLicpOwogICAgICAgIH0gZWxzZSB7CiAgICAgICAgICB0aGlzLl9hZGRQcm9wZXJ0eUVmZmVjdChwcm9wZXJ0eSwgVFlQRVMuUkVGTEVDVCwgewogICAgICAgICAgICBmbjogcnVuUmVmbGVjdEVmZmVjdCwKICAgICAgICAgICAgaW5mbzogewogICAgICAgICAgICAgIGF0dHJOYW1lOiBhdHRyCiAgICAgICAgICAgIH0KICAgICAgICAgIH0pOwogICAgICAgIH0KICAgICAgfQoKICAgICAgLyoqCiAgICAgICAqIEVxdWl2YWxlbnQgdG8gc3RhdGljIGBjcmVhdGVDb21wdXRlZFByb3BlcnR5YCBBUEkgYnV0IGNhbiBiZSBjYWxsZWQgb24KICAgICAgICogYW4gaW5zdGFuY2UgdG8gYWRkIGVmZmVjdHMgYXQgcnVudGltZS4gIFNlZSB0aGF0IG1ldGhvZCBmb3IKICAgICAgICogZnVsbCBBUEkgZG9jcy4KICAgICAgICoKICAgICAgICogQHBhcmFtIHtzdHJpbmd9IHByb3BlcnR5IE5hbWUgb2YgY29tcHV0ZWQgcHJvcGVydHkgdG8gc2V0CiAgICAgICAqIEBwYXJhbSB7c3RyaW5nfSBleHByZXNzaW9uIE1ldGhvZCBleHByZXNzaW9uCiAgICAgICAqIEBwYXJhbSB7Ym9vbGVhbnxPYmplY3Q9fSBkeW5hbWljRm4gQm9vbGVhbiBvciBvYmplY3QgbWFwIGluZGljYXRpbmcKICAgICAgICogICB3aGV0aGVyIG1ldGhvZCBuYW1lcyBzaG91bGQgYmUgaW5jbHVkZWQgYXMgYSBkZXBlbmRlbmN5IHRvIHRoZSBlZmZlY3QuCiAgICAgICAqIEByZXR1cm4ge3ZvaWR9CiAgICAgICAqIEBwcm90ZWN0ZWQKICAgICAgICovCiAgICAgIF9jcmVhdGVDb21wdXRlZFByb3BlcnR5KHByb3BlcnR5LCBleHByZXNzaW9uLCBkeW5hbWljRm4pIHsKICAgICAgICBsZXQgc2lnID0gcGFyc2VNZXRob2QoZXhwcmVzc2lvbik7CiAgICAgICAgaWYgKCFzaWcpIHsKICAgICAgICAgIHRocm93IG5ldyBFcnJvcigiTWFsZm9ybWVkIGNvbXB1dGVkIGV4cHJlc3Npb24gJyIgKyBleHByZXNzaW9uICsgIiciKTsKICAgICAgICB9CiAgICAgICAgY3JlYXRlTWV0aG9kRWZmZWN0KHRoaXMsIHNpZywgVFlQRVMuQ09NUFVURSwgcnVuQ29tcHV0ZWRFZmZlY3QsIHByb3BlcnR5LCBkeW5hbWljRm4pOwogICAgICB9CgogICAgICAvKioKICAgICAgICogR2F0aGVyIHRoZSBhcmd1bWVudCB2YWx1ZXMgZm9yIGEgbWV0aG9kIHNwZWNpZmllZCBpbiB0aGUgcHJvdmlkZWQgYXJyYXkKICAgICAgICogb2YgYXJndW1lbnQgbWV0YWRhdGEuCiAgICAgICAqCiAgICAgICAqIFRoZSBgcGF0aGAgYW5kIGB2YWx1ZWAgYXJndW1lbnRzIGFyZSB1c2VkIHRvIGZpbGwgaW4gd2lsZGNhcmQgZGVzY3JpcHRvcgogICAgICAgKiB3aGVuIHRoZSBtZXRob2QgaXMgYmVpbmcgY2FsbGVkIGFzIGEgcmVzdWx0IG9mIGEgcGF0aCBub3RpZmljYXRpb24uCiAgICAgICAqCiAgICAgICAqIEBwYXJhbSB7IUFycmF5PCFNZXRob2RBcmc+fSBhcmdzIEFycmF5IG9mIGFyZ3VtZW50IG1ldGFkYXRhCiAgICAgICAqIEBwYXJhbSB7c3RyaW5nfSBwYXRoIFByb3BlcnR5L3BhdGggbmFtZSB0aGF0IHRyaWdnZXJlZCB0aGUgbWV0aG9kIGVmZmVjdAogICAgICAgKiBAcGFyYW0ge09iamVjdH0gcHJvcHMgQmFnIG9mIGN1cnJlbnQgcHJvcGVydHkgY2hhbmdlcwogICAgICAgKiBAcmV0dXJuIHtBcnJheTwqPn0gQXJyYXkgb2YgYXJndW1lbnQgdmFsdWVzCiAgICAgICAqIEBwcml2YXRlCiAgICAgICAqLwogICAgICBfbWFyc2hhbEFyZ3MoYXJncywgcGF0aCwgcHJvcHMpIHsKICAgICAgICBjb25zdCBkYXRhID0gdGhpcy5fX2RhdGE7CiAgICAgICAgbGV0IHZhbHVlcyA9IFtdOwogICAgICAgIGZvciAobGV0IGk9MCwgbD1hcmdzLmxlbmd0aDsgaTxsOyBpKyspIHsKICAgICAgICAgIGxldCBhcmcgPSBhcmdzW2ldOwogICAgICAgICAgbGV0IG5hbWUgPSBhcmcubmFtZTsKICAgICAgICAgIGxldCB2OwogICAgICAgICAgaWYgKGFyZy5saXRlcmFsKSB7CiAgICAgICAgICAgIHYgPSBhcmcudmFsdWU7CiAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICBpZiAoYXJnLnN0cnVjdHVyZWQpIHsKICAgICAgICAgICAgICB2ID0gUG9seW1lci5QYXRoLmdldChkYXRhLCBuYW1lKTsKICAgICAgICAgICAgICAvLyB3aGVuIGRhdGEgaXMgbm90IHN0b3JlZCBlLmcuIGBzcGxpY2VzYAogICAgICAgICAgICAgIGlmICh2ID09PSB1bmRlZmluZWQpIHsKICAgICAgICAgICAgICAgIHYgPSBwcm9wc1tuYW1lXTsKICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgICAgdiA9IGRhdGFbbmFtZV07CiAgICAgICAgICAgIH0KICAgICAgICAgIH0KICAgICAgICAgIGlmIChhcmcud2lsZGNhcmQpIHsKICAgICAgICAgICAgLy8gT25seSBzZW5kIHRoZSBhY3R1YWwgcGF0aCBjaGFuZ2VkIGluZm8gaWYgdGhlIGNoYW5nZSB0aGF0CiAgICAgICAgICAgIC8vIGNhdXNlZCB0aGUgb2JzZXJ2ZXIgdG8gcnVuIG1hdGNoZWQgdGhlIHdpbGRjYXJkCiAgICAgICAgICAgIGxldCBiYXNlQ2hhbmdlZCA9IChuYW1lLmluZGV4T2YocGF0aCArICcuJykgPT09IDApOwogICAgICAgICAgICBsZXQgbWF0Y2hlcyA9IChwYXRoLmluZGV4T2YobmFtZSkgPT09IDAgJiYgIWJhc2VDaGFuZ2VkKTsKICAgICAgICAgICAgdmFsdWVzW2ldID0gewogICAgICAgICAgICAgIHBhdGg6IG1hdGNoZXMgPyBwYXRoIDogbmFtZSwKICAgICAgICAgICAgICB2YWx1ZTogbWF0Y2hlcyA/IHByb3BzW3BhdGhdIDogdiwKICAgICAgICAgICAgICBiYXNlOiB2CiAgICAgICAgICAgIH07CiAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICB2YWx1ZXNbaV0gPSB2OwogICAgICAgICAgfQogICAgICAgIH0KICAgICAgICByZXR1cm4gdmFsdWVzOwogICAgICB9CgogICAgICAvLyAtLSBzdGF0aWMgY2xhc3MgbWV0aG9kcyAtLS0tLS0tLS0tLS0KCiAgICAgIC8qKgogICAgICAgKiBFbnN1cmVzIGFuIGFjY2Vzc29yIGV4aXN0cyBmb3IgdGhlIHNwZWNpZmllZCBwcm9wZXJ0eSwgYW5kIGFkZHMKICAgICAgICogdG8gYSBsaXN0IG9mICJwcm9wZXJ0eSBlZmZlY3RzIiB0aGF0IHdpbGwgcnVuIHdoZW4gdGhlIGFjY2Vzc29yIGZvcgogICAgICAgKiB0aGUgc3BlY2lmaWVkIHByb3BlcnR5IGlzIHNldC4gIEVmZmVjdHMgYXJlIGdyb3VwZWQgYnkgInR5cGUiLCB3aGljaAogICAgICAgKiByb3VnaGx5IGNvcnJlc3BvbmRzIHRvIGEgcGhhc2UgaW4gZWZmZWN0IHByb2Nlc3NpbmcuICBUaGUgZWZmZWN0CiAgICAgICAqIG1ldGFkYXRhIHNob3VsZCBiZSBpbiB0aGUgZm9sbG93aW5nIGZvcm06CiAgICAgICAqCiAgICAgICAqICAgICB7CiAgICAgICAqICAgICAgIGZuOiBlZmZlY3RGdW5jdGlvbiwgLy8gUmVmZXJlbmNlIHRvIGZ1bmN0aW9uIHRvIGNhbGwgdG8gcGVyZm9ybSBlZmZlY3QKICAgICAgICogICAgICAgaW5mbzogeyAuLi4gfSAgICAgICAvLyBFZmZlY3QgbWV0YWRhdGEgcGFzc2VkIHRvIGZ1bmN0aW9uCiAgICAgICAqICAgICAgIHRyaWdnZXI6IHsgICAgICAgICAgLy8gT3B0aW9uYWwgdHJpZ2dlcmluZyBtZXRhZGF0YTsgaWYgbm90IHByb3ZpZGVkCiAgICAgICAqICAgICAgICAgbmFtZTogc3RyaW5nICAgICAgLy8gdGhlIHByb3BlcnR5IGlzIHRyZWF0ZWQgYXMgYSB3aWxkY2FyZAogICAgICAgKiAgICAgICAgIHN0cnVjdHVyZWQ6IGJvb2xlYW4KICAgICAgICogICAgICAgICB3aWxkY2FyZDogYm9vbGVhbgogICAgICAgKiAgICAgICB9CiAgICAgICAqICAgICB9CiAgICAgICAqCiAgICAgICAqIEVmZmVjdHMgYXJlIGNhbGxlZCBmcm9tIGBfcHJvcGVydGllc0NoYW5nZWRgIGluIHRoZSBmb2xsb3dpbmcgb3JkZXIgYnkKICAgICAgICogdHlwZToKICAgICAgICoKICAgICAgICogMS4gQ09NUFVURQogICAgICAgKiAyLiBQUk9QQUdBVEUKICAgICAgICogMy4gUkVGTEVDVAogICAgICAgKiA0LiBPQlNFUlZFCiAgICAgICAqIDUuIE5PVElGWQogICAgICAgKgogICAgICAgKiBFZmZlY3QgZnVuY3Rpb25zIGFyZSBjYWxsZWQgd2l0aCB0aGUgZm9sbG93aW5nIHNpZ25hdHVyZToKICAgICAgICoKICAgICAgICogICAgIGVmZmVjdEZ1bmN0aW9uKGluc3QsIHBhdGgsIHByb3BzLCBvbGRQcm9wcywgaW5mbywgaGFzUGF0aHMpCiAgICAgICAqCiAgICAgICAqIEBwYXJhbSB7c3RyaW5nfSBwcm9wZXJ0eSBQcm9wZXJ0eSB0aGF0IHNob3VsZCB0cmlnZ2VyIHRoZSBlZmZlY3QKICAgICAgICogQHBhcmFtIHtzdHJpbmd9IHR5cGUgRWZmZWN0IHR5cGUsIGZyb20gdGhpcy5QUk9QRVJUWV9FRkZFQ1RfVFlQRVMKICAgICAgICogQHBhcmFtIHtPYmplY3Q9fSBlZmZlY3QgRWZmZWN0IG1ldGFkYXRhIG9iamVjdAogICAgICAgKiBAcmV0dXJuIHt2b2lkfQogICAgICAgKiBAcHJvdGVjdGVkCiAgICAgICAqLwogICAgICBzdGF0aWMgYWRkUHJvcGVydHlFZmZlY3QocHJvcGVydHksIHR5cGUsIGVmZmVjdCkgewogICAgICAgIHRoaXMucHJvdG90eXBlLl9hZGRQcm9wZXJ0eUVmZmVjdChwcm9wZXJ0eSwgdHlwZSwgZWZmZWN0KTsKICAgICAgfQoKICAgICAgLyoqCiAgICAgICAqIENyZWF0ZXMgYSBzaW5nbGUtcHJvcGVydHkgb2JzZXJ2ZXIgZm9yIHRoZSBnaXZlbiBwcm9wZXJ0eS4KICAgICAgICoKICAgICAgICogQHBhcmFtIHtzdHJpbmd9IHByb3BlcnR5IFByb3BlcnR5IG5hbWUKICAgICAgICogQHBhcmFtIHtzdHJpbmd8ZnVuY3Rpb24oKiwqKX0gbWV0aG9kIEZ1bmN0aW9uIG9yIG5hbWUgb2Ygb2JzZXJ2ZXIgbWV0aG9kIHRvIGNhbGwKICAgICAgICogQHBhcmFtIHtib29sZWFuPX0gZHluYW1pY0ZuIFdoZXRoZXIgdGhlIG1ldGhvZCBuYW1lIHNob3VsZCBiZSBpbmNsdWRlZCBhcwogICAgICAgKiAgIGEgZGVwZW5kZW5jeSB0byB0aGUgZWZmZWN0LgogICAgICAgKiBAcmV0dXJuIHt2b2lkfQogICAgICAgKiBAcHJvdGVjdGVkCiAgICAgICAqLwogICAgICBzdGF0aWMgY3JlYXRlUHJvcGVydHlPYnNlcnZlcihwcm9wZXJ0eSwgbWV0aG9kLCBkeW5hbWljRm4pIHsKICAgICAgICB0aGlzLnByb3RvdHlwZS5fY3JlYXRlUHJvcGVydHlPYnNlcnZlcihwcm9wZXJ0eSwgbWV0aG9kLCBkeW5hbWljRm4pOwogICAgICB9CgogICAgICAvKioKICAgICAgICogQ3JlYXRlcyBhIG11bHRpLXByb3BlcnR5ICJtZXRob2Qgb2JzZXJ2ZXIiIGJhc2VkIG9uIHRoZSBwcm92aWRlZAogICAgICAgKiBleHByZXNzaW9uLCB3aGljaCBzaG91bGQgYmUgYSBzdHJpbmcgaW4gdGhlIGZvcm0gb2YgYSBub3JtYWwgSmF2YVNjcmlwdAogICAgICAgKiBmdW5jdGlvbiBzaWduYXR1cmU6IGAnbWV0aG9kTmFtZShhcmcxLCBbLi4uLCBhcmduXSknYC4gIEVhY2ggYXJndW1lbnQKICAgICAgICogc2hvdWxkIGNvcnJlc3BvbmQgdG8gYSBwcm9wZXJ0eSBvciBwYXRoIGluIHRoZSBjb250ZXh0IG9mIHRoaXMKICAgICAgICogcHJvdG90eXBlIChvciBpbnN0YW5jZSksIG9yIG1heSBiZSBhIGxpdGVyYWwgc3RyaW5nIG9yIG51bWJlci4KICAgICAgICoKICAgICAgICogQHBhcmFtIHtzdHJpbmd9IGV4cHJlc3Npb24gTWV0aG9kIGV4cHJlc3Npb24KICAgICAgICogQHBhcmFtIHtib29sZWFufE9iamVjdD19IGR5bmFtaWNGbiBCb29sZWFuIG9yIG9iamVjdCBtYXAgaW5kaWNhdGluZwogICAgICAgKiBAcmV0dXJuIHt2b2lkfQogICAgICAgKiAgIHdoZXRoZXIgbWV0aG9kIG5hbWVzIHNob3VsZCBiZSBpbmNsdWRlZCBhcyBhIGRlcGVuZGVuY3kgdG8gdGhlIGVmZmVjdC4KICAgICAgICogQHByb3RlY3RlZAogICAgICAgKi8KICAgICAgc3RhdGljIGNyZWF0ZU1ldGhvZE9ic2VydmVyKGV4cHJlc3Npb24sIGR5bmFtaWNGbikgewogICAgICAgIHRoaXMucHJvdG90eXBlLl9jcmVhdGVNZXRob2RPYnNlcnZlcihleHByZXNzaW9uLCBkeW5hbWljRm4pOwogICAgICB9CgogICAgICAvKioKICAgICAgICogQ2F1c2VzIHRoZSBzZXR0ZXIgZm9yIHRoZSBnaXZlbiBwcm9wZXJ0eSB0byBkaXNwYXRjaCBgPHByb3BlcnR5Pi1jaGFuZ2VkYAogICAgICAgKiBldmVudHMgdG8gbm90aWZ5IG9mIGNoYW5nZXMgdG8gdGhlIHByb3BlcnR5LgogICAgICAgKgogICAgICAgKiBAcGFyYW0ge3N0cmluZ30gcHJvcGVydHkgUHJvcGVydHkgbmFtZQogICAgICAgKiBAcmV0dXJuIHt2b2lkfQogICAgICAgKiBAcHJvdGVjdGVkCiAgICAgICAqLwogICAgICBzdGF0aWMgY3JlYXRlTm90aWZ5aW5nUHJvcGVydHkocHJvcGVydHkpIHsKICAgICAgICB0aGlzLnByb3RvdHlwZS5fY3JlYXRlTm90aWZ5aW5nUHJvcGVydHkocHJvcGVydHkpOwogICAgICB9CgogICAgICAvKioKICAgICAgICogQ3JlYXRlcyBhIHJlYWQtb25seSBhY2Nlc3NvciBmb3IgdGhlIGdpdmVuIHByb3BlcnR5LgogICAgICAgKgogICAgICAgKiBUbyBzZXQgdGhlIHByb3BlcnR5LCB1c2UgdGhlIHByb3RlY3RlZCBgX3NldFByb3BlcnR5YCBBUEkuCiAgICAgICAqIFRvIGNyZWF0ZSBhIGN1c3RvbSBwcm90ZWN0ZWQgc2V0dGVyIChlLmcuIGBfc2V0TXlQcm9wKClgIGZvcgogICAgICAgKiBwcm9wZXJ0eSBgbXlQcm9wYCksIHBhc3MgYHRydWVgIGZvciBgcHJvdGVjdGVkU2V0dGVyYC4KICAgICAgICoKICAgICAgICogTm90ZSwgaWYgdGhlIHByb3BlcnR5IHdpbGwgaGF2ZSBvdGhlciBwcm9wZXJ0eSBlZmZlY3RzLCB0aGlzIG1ldGhvZAogICAgICAgKiBzaG91bGQgYmUgY2FsbGVkIGZpcnN0LCBiZWZvcmUgYWRkaW5nIG90aGVyIGVmZmVjdHMuCiAgICAgICAqCiAgICAgICAqIEBwYXJhbSB7c3RyaW5nfSBwcm9wZXJ0eSBQcm9wZXJ0eSBuYW1lCiAgICAgICAqIEBwYXJhbSB7Ym9vbGVhbj19IHByb3RlY3RlZFNldHRlciBDcmVhdGVzIGEgY3VzdG9tIHByb3RlY3RlZCBzZXR0ZXIKICAgICAgICogICB3aGVuIGB0cnVlYC4KICAgICAgICogQHJldHVybiB7dm9pZH0KICAgICAgICogQHByb3RlY3RlZAogICAgICAgKi8KICAgICAgc3RhdGljIGNyZWF0ZVJlYWRPbmx5UHJvcGVydHkocHJvcGVydHksIHByb3RlY3RlZFNldHRlcikgewogICAgICAgIHRoaXMucHJvdG90eXBlLl9jcmVhdGVSZWFkT25seVByb3BlcnR5KHByb3BlcnR5LCBwcm90ZWN0ZWRTZXR0ZXIpOwogICAgICB9CgogICAgICAvKioKICAgICAgICogQ2F1c2VzIHRoZSBzZXR0ZXIgZm9yIHRoZSBnaXZlbiBwcm9wZXJ0eSB0byByZWZsZWN0IHRoZSBwcm9wZXJ0eSB2YWx1ZQogICAgICAgKiB0byBhIChkYXNoLWNhc2VkKSBhdHRyaWJ1dGUgb2YgdGhlIHNhbWUgbmFtZS4KICAgICAgICoKICAgICAgICogQHBhcmFtIHtzdHJpbmd9IHByb3BlcnR5IFByb3BlcnR5IG5hbWUKICAgICAgICogQHJldHVybiB7dm9pZH0KICAgICAgICogQHByb3RlY3RlZAogICAgICAgKi8KICAgICAgc3RhdGljIGNyZWF0ZVJlZmxlY3RlZFByb3BlcnR5KHByb3BlcnR5KSB7CiAgICAgICAgdGhpcy5wcm90b3R5cGUuX2NyZWF0ZVJlZmxlY3RlZFByb3BlcnR5KHByb3BlcnR5KTsKICAgICAgfQoKICAgICAgLyoqCiAgICAgICAqIENyZWF0ZXMgYSBjb21wdXRlZCBwcm9wZXJ0eSB3aG9zZSB2YWx1ZSBpcyBzZXQgdG8gdGhlIHJlc3VsdCBvZiB0aGUKICAgICAgICogbWV0aG9kIGRlc2NyaWJlZCBieSB0aGUgZ2l2ZW4gYGV4cHJlc3Npb25gIGVhY2ggdGltZSBvbmUgb3IgbW9yZQogICAgICAgKiBhcmd1bWVudHMgdG8gdGhlIG1ldGhvZCBjaGFuZ2VzLiAgVGhlIGV4cHJlc3Npb24gc2hvdWxkIGJlIGEgc3RyaW5nCiAgICAgICAqIGluIHRoZSBmb3JtIG9mIGEgbm9ybWFsIEphdmFTY3JpcHQgZnVuY3Rpb24gc2lnbmF0dXJlOgogICAgICAgKiBgJ21ldGhvZE5hbWUoYXJnMSwgWy4uLiwgYXJnbl0pJ2AKICAgICAgICoKICAgICAgICogQHBhcmFtIHtzdHJpbmd9IHByb3BlcnR5IE5hbWUgb2YgY29tcHV0ZWQgcHJvcGVydHkgdG8gc2V0CiAgICAgICAqIEBwYXJhbSB7c3RyaW5nfSBleHByZXNzaW9uIE1ldGhvZCBleHByZXNzaW9uCiAgICAgICAqIEBwYXJhbSB7Ym9vbGVhbnxPYmplY3Q9fSBkeW5hbWljRm4gQm9vbGVhbiBvciBvYmplY3QgbWFwIGluZGljYXRpbmcgd2hldGhlcgogICAgICAgKiAgIG1ldGhvZCBuYW1lcyBzaG91bGQgYmUgaW5jbHVkZWQgYXMgYSBkZXBlbmRlbmN5IHRvIHRoZSBlZmZlY3QuCiAgICAgICAqIEByZXR1cm4ge3ZvaWR9CiAgICAgICAqIEBwcm90ZWN0ZWQKICAgICAgICovCiAgICAgIHN0YXRpYyBjcmVhdGVDb21wdXRlZFByb3BlcnR5KHByb3BlcnR5LCBleHByZXNzaW9uLCBkeW5hbWljRm4pIHsKICAgICAgICB0aGlzLnByb3RvdHlwZS5fY3JlYXRlQ29tcHV0ZWRQcm9wZXJ0eShwcm9wZXJ0eSwgZXhwcmVzc2lvbiwgZHluYW1pY0ZuKTsKICAgICAgfQoKICAgICAgLyoqCiAgICAgICAqIFBhcnNlcyB0aGUgcHJvdmlkZWQgdGVtcGxhdGUgdG8gZW5zdXJlIGJpbmRpbmcgZWZmZWN0cyBhcmUgY3JlYXRlZAogICAgICAgKiBmb3IgdGhlbSwgYW5kIHRoZW4gZW5zdXJlcyBwcm9wZXJ0eSBhY2Nlc3NvcnMgYXJlIGNyZWF0ZWQgZm9yIGFueQogICAgICAgKiBkZXBlbmRlbnQgcHJvcGVydGllcyBpbiB0aGUgdGVtcGxhdGUuICBCaW5kaW5nIGVmZmVjdHMgZm9yIGJvdW5kCiAgICAgICAqIHRlbXBsYXRlcyBhcmUgc3RvcmVkIGluIGEgbGlua2VkIGxpc3Qgb24gdGhlIGluc3RhbmNlIHNvIHRoYXQKICAgICAgICogdGVtcGxhdGVzIGNhbiBiZSBlZmZpY2llbnRseSBzdGFtcGVkIGFuZCB1bnN0YW1wZWQuCiAgICAgICAqCiAgICAgICAqIEBwYXJhbSB7IUhUTUxUZW1wbGF0ZUVsZW1lbnR9IHRlbXBsYXRlIFRlbXBsYXRlIGNvbnRhaW5pbmcgYmluZGluZwogICAgICAgKiAgIGJpbmRpbmdzCiAgICAgICAqIEByZXR1cm4geyFUZW1wbGF0ZUluZm99IFRlbXBsYXRlIG1ldGFkYXRhIG9iamVjdAogICAgICAgKiBAcHJvdGVjdGVkCiAgICAgICAqLwogICAgICBzdGF0aWMgYmluZFRlbXBsYXRlKHRlbXBsYXRlKSB7CiAgICAgICAgcmV0dXJuIHRoaXMucHJvdG90eXBlLl9iaW5kVGVtcGxhdGUodGVtcGxhdGUpOwogICAgICB9CgogICAgICAvLyAtLSBiaW5kaW5nIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiAgICAgIC8qKgogICAgICAgKiBFcXVpdmFsZW50IHRvIHN0YXRpYyBgYmluZFRlbXBsYXRlYCBBUEkgYnV0IGNhbiBiZSBjYWxsZWQgb24KICAgICAgICogYW4gaW5zdGFuY2UgdG8gYWRkIGVmZmVjdHMgYXQgcnVudGltZS4gIFNlZSB0aGF0IG1ldGhvZCBmb3IKICAgICAgICogZnVsbCBBUEkgZG9jcy4KICAgICAgICoKICAgICAgICogVGhpcyBtZXRob2QgbWF5IGJlIGNhbGxlZCBvbiB0aGUgcHJvdG90eXBlIChmb3IgcHJvdG90eXBpY2FsIHRlbXBsYXRlCiAgICAgICAqIGJpbmRpbmcsIHRvIGF2b2lkIGNyZWF0aW5nIGFjY2Vzc29ycyBldmVyeSBpbnN0YW5jZSkgb25jZSBwZXIgcHJvdG90eXBlLAogICAgICAgKiBhbmQgd2lsbCBiZSBjYWxsZWQgd2l0aCBgcnVudGltZUJpbmRpbmc6IHRydWVgIGJ5IGBfc3RhbXBUZW1wbGF0ZWAgdG8KICAgICAgICogY3JlYXRlIGFuZCBsaW5rIGFuIGluc3RhbmNlIG9mIHRoZSB0ZW1wbGF0ZSBtZXRhZGF0YSBhc3NvY2lhdGVkIHdpdGggYQogICAgICAgKiBwYXJ0aWN1bGFyIHN0YW1waW5nLgogICAgICAgKgogICAgICAgKiBAcGFyYW0geyFIVE1MVGVtcGxhdGVFbGVtZW50fSB0ZW1wbGF0ZSBUZW1wbGF0ZSBjb250YWluaW5nIGJpbmRpbmcKICAgICAgICogICBiaW5kaW5ncwogICAgICAgKiBAcGFyYW0ge2Jvb2xlYW49fSBpbnN0YW5jZUJpbmRpbmcgV2hlbiBmYWxzZSAoZGVmYXVsdCksIHBlcmZvcm1zCiAgICAgICAqICAgInByb3RvdHlwaWNhbCIgYmluZGluZyBvZiB0aGUgdGVtcGxhdGUgYW5kIG92ZXJ3cml0ZXMgYW55IHByZXZpb3VzbHkKICAgICAgICogICBib3VuZCB0ZW1wbGF0ZSBmb3IgdGhlIGNsYXNzLiBXaGVuIHRydWUgKGFzIHBhc3NlZCBmcm9tCiAgICAgICAqICAgYF9zdGFtcFRlbXBsYXRlYCksIHRoZSB0ZW1wbGF0ZSBpbmZvIGlzIGluc3RhbmNlZCBhbmQgbGlua2VkIGludG8KICAgICAgICogICB0aGUgbGlzdCBvZiBib3VuZCB0ZW1wbGF0ZXMuCiAgICAgICAqIEByZXR1cm4geyFUZW1wbGF0ZUluZm99IFRlbXBsYXRlIG1ldGFkYXRhIG9iamVjdDsgZm9yIGBydW50aW1lQmluZGluZ2AsCiAgICAgICAqICAgdGhpcyBpcyBhbiBpbnN0YW5jZSBvZiB0aGUgcHJvdG90eXBpY2FsIHRlbXBsYXRlIGluZm8KICAgICAgICogQHByb3RlY3RlZAogICAgICAgKi8KICAgICAgX2JpbmRUZW1wbGF0ZSh0ZW1wbGF0ZSwgaW5zdGFuY2VCaW5kaW5nKSB7CiAgICAgICAgbGV0IHRlbXBsYXRlSW5mbyA9IHRoaXMuY29uc3RydWN0b3IuX3BhcnNlVGVtcGxhdGUodGVtcGxhdGUpOwogICAgICAgIGxldCB3YXNQcmVCb3VuZCA9IHRoaXMuX190ZW1wbGF0ZUluZm8gPT0gdGVtcGxhdGVJbmZvOwogICAgICAgIC8vIE9wdGltaXphdGlvbjogc2luY2UgdGhpcyBpcyBjYWxsZWQgdHdpY2UgZm9yIHByb3RvLWJvdW5kIHRlbXBsYXRlcywKICAgICAgICAvLyBkb24ndCBhdHRlbXB0IHRvIHJlY3JlYXRlIGFjY2Vzc29ycyBpZiB0aGlzIHRlbXBsYXRlIHdhcyBwcmUtYm91bmQKICAgICAgICBpZiAoIXdhc1ByZUJvdW5kKSB7CiAgICAgICAgICBmb3IgKGxldCBwcm9wIGluIHRlbXBsYXRlSW5mby5wcm9wZXJ0eUVmZmVjdHMpIHsKICAgICAgICAgICAgdGhpcy5fY3JlYXRlUHJvcGVydHlBY2Nlc3Nvcihwcm9wKTsKICAgICAgICAgIH0KICAgICAgICB9CiAgICAgICAgaWYgKGluc3RhbmNlQmluZGluZykgewogICAgICAgICAgLy8gRm9yIGluc3RhbmNlLXRpbWUgYmluZGluZywgY3JlYXRlIGluc3RhbmNlIG9mIHRlbXBsYXRlIG1ldGFkYXRhCiAgICAgICAgICAvLyBhbmQgbGluayBpbnRvIGxpc3Qgb2YgdGVtcGxhdGVzIGlmIG5lY2Vzc2FyeQogICAgICAgICAgdGVtcGxhdGVJbmZvID0gLyoqIEB0eXBlIHshVGVtcGxhdGVJbmZvfSAqLyhPYmplY3QuY3JlYXRlKHRlbXBsYXRlSW5mbykpOwogICAgICAgICAgdGVtcGxhdGVJbmZvLndhc1ByZUJvdW5kID0gd2FzUHJlQm91bmQ7CiAgICAgICAgICBpZiAoIXdhc1ByZUJvdW5kICYmIHRoaXMuX190ZW1wbGF0ZUluZm8pIHsKICAgICAgICAgICAgbGV0IGxhc3QgPSB0aGlzLl9fdGVtcGxhdGVJbmZvTGFzdCB8fCB0aGlzLl9fdGVtcGxhdGVJbmZvOwogICAgICAgICAgICB0aGlzLl9fdGVtcGxhdGVJbmZvTGFzdCA9IGxhc3QubmV4dFRlbXBsYXRlSW5mbyA9IHRlbXBsYXRlSW5mbzsKICAgICAgICAgICAgdGVtcGxhdGVJbmZvLnByZXZpb3VzVGVtcGxhdGVJbmZvID0gbGFzdDsKICAgICAgICAgICAgcmV0dXJuIHRlbXBsYXRlSW5mbzsKICAgICAgICAgIH0KICAgICAgICB9CiAgICAgICAgcmV0dXJuIHRoaXMuX190ZW1wbGF0ZUluZm8gPSB0ZW1wbGF0ZUluZm87CiAgICAgIH0KCiAgICAgIC8qKgogICAgICAgKiBBZGRzIGEgcHJvcGVydHkgZWZmZWN0IHRvIHRoZSBnaXZlbiB0ZW1wbGF0ZSBtZXRhZGF0YSwgd2hpY2ggaXMgcnVuCiAgICAgICAqIGF0IHRoZSAicHJvcGFnYXRlIiBzdGFnZSBvZiBgX3Byb3BlcnRpZXNDaGFuZ2VkYCB3aGVuIHRoZSB0ZW1wbGF0ZQogICAgICAgKiBoYXMgYmVlbiBib3VuZCB0byB0aGUgZWxlbWVudCB2aWEgYF9iaW5kVGVtcGxhdGVgLgogICAgICAgKgogICAgICAgKiBUaGUgYGVmZmVjdGAgb2JqZWN0IHNob3VsZCBtYXRjaCB0aGUgZm9ybWF0IGluIGBfYWRkUHJvcGVydHlFZmZlY3RgLgogICAgICAgKgogICAgICAgKiBAcGFyYW0ge09iamVjdH0gdGVtcGxhdGVJbmZvIFRlbXBsYXRlIG1ldGFkYXRhIHRvIGFkZCBlZmZlY3QgdG8KICAgICAgICogQHBhcmFtIHtzdHJpbmd9IHByb3AgUHJvcGVydHkgdGhhdCBzaG91bGQgdHJpZ2dlciB0aGUgZWZmZWN0CiAgICAgICAqIEBwYXJhbSB7T2JqZWN0PX0gZWZmZWN0IEVmZmVjdCBtZXRhZGF0YSBvYmplY3QKICAgICAgICogQHJldHVybiB7dm9pZH0KICAgICAgICogQHByb3RlY3RlZAogICAgICAgKi8KICAgICAgc3RhdGljIF9hZGRUZW1wbGF0ZVByb3BlcnR5RWZmZWN0KHRlbXBsYXRlSW5mbywgcHJvcCwgZWZmZWN0KSB7CiAgICAgICAgbGV0IGhvc3RQcm9wcyA9IHRlbXBsYXRlSW5mby5ob3N0UHJvcHMgPSB0ZW1wbGF0ZUluZm8uaG9zdFByb3BzIHx8IHt9OwogICAgICAgIGhvc3RQcm9wc1twcm9wXSA9IHRydWU7CiAgICAgICAgbGV0IGVmZmVjdHMgPSB0ZW1wbGF0ZUluZm8ucHJvcGVydHlFZmZlY3RzID0gdGVtcGxhdGVJbmZvLnByb3BlcnR5RWZmZWN0cyB8fCB7fTsKICAgICAgICBsZXQgcHJvcEVmZmVjdHMgPSBlZmZlY3RzW3Byb3BdID0gZWZmZWN0c1twcm9wXSB8fCBbXTsKICAgICAgICBwcm9wRWZmZWN0cy5wdXNoKGVmZmVjdCk7CiAgICAgIH0KCiAgICAgIC8qKgogICAgICAgKiBTdGFtcHMgdGhlIHByb3ZpZGVkIHRlbXBsYXRlIGFuZCBwZXJmb3JtcyBpbnN0YW5jZS10aW1lIHNldHVwIGZvcgogICAgICAgKiBQb2x5bWVyIHRlbXBsYXRlIGZlYXR1cmVzLCBpbmNsdWRpbmcgZGF0YSBiaW5kaW5ncywgZGVjbGFyYXRpdmUgZXZlbnQKICAgICAgICogbGlzdGVuZXJzLCBhbmQgdGhlIGB0aGlzLiRgIG1hcCBvZiBgaWRgJ3MgdG8gbm9kZXMuICBBIGRvY3VtZW50IGZyYWdtZW50CiAgICAgICAqIGlzIHJldHVybmVkIGNvbnRhaW5pbmcgdGhlIHN0YW1wZWQgRE9NLCByZWFkeSBmb3IgaW5zZXJ0aW9uIGludG8gdGhlCiAgICAgICAqIERPTS4KICAgICAgICoKICAgICAgICogVGhpcyBtZXRob2QgbWF5IGJlIGNhbGxlZCBtb3JlIHRoYW4gb25jZTsgaG93ZXZlciBub3RlIHRoYXQgZHVlIHRvCiAgICAgICAqIGBzaGFkeWNzc2AgcG9seWZpbGwgbGltaXRhdGlvbnMsIG9ubHkgc3R5bGVzIGZyb20gdGVtcGxhdGVzIHByZXBhcmVkCiAgICAgICAqIHVzaW5nIGBTaGFkeUNTUy5wcmVwYXJlVGVtcGxhdGVgIHdpbGwgYmUgY29ycmVjdGx5IHBvbHlmaWxsZWQgKHNjb3BlZAogICAgICAgKiB0byB0aGUgc2hhZG93IHJvb3QgYW5kIHN1cHBvcnQgQ1NTIGN1c3RvbSBwcm9wZXJ0aWVzKSwgYW5kIG5vdGUgdGhhdAogICAgICAgKiBgU2hhZHlDU1MucHJlcGFyZVRlbXBsYXRlYCBtYXkgb25seSBiZSBjYWxsZWQgb25jZSBwZXIgZWxlbWVudC4gQXMgc3VjaCwKICAgICAgICogYW55IHN0eWxlcyByZXF1aXJlZCBieSBpbiBydW50aW1lLXN0YW1wZWQgdGVtcGxhdGVzIG11c3QgYmUgaW5jbHVkZWQKICAgICAgICogaW4gdGhlIG1haW4gZWxlbWVudCB0ZW1wbGF0ZS4KICAgICAgICoKICAgICAgICogQHBhcmFtIHshSFRNTFRlbXBsYXRlRWxlbWVudH0gdGVtcGxhdGUgVGVtcGxhdGUgdG8gc3RhbXAKICAgICAgICogQHJldHVybiB7IVN0YW1wZWRUZW1wbGF0ZX0gQ2xvbmVkIHRlbXBsYXRlIGNvbnRlbnQKICAgICAgICogQG92ZXJyaWRlCiAgICAgICAqIEBwcm90ZWN0ZWQKICAgICAgICovCiAgICAgIF9zdGFtcFRlbXBsYXRlKHRlbXBsYXRlKSB7CiAgICAgICAgLy8gRW5zdXJlcyB0aGF0IGNyZWF0ZWQgZG9tIGlzIGBfZW5xdWV1ZUNsaWVudGAnZCB0byB0aGlzIGVsZW1lbnQgc28KICAgICAgICAvLyB0aGF0IGl0IGNhbiBiZSBmbHVzaGVkIG9uIG5leHQgY2FsbCB0byBgX2ZsdXNoUHJvcGVydGllc2AKICAgICAgICBob3N0U3RhY2suYmVnaW5Ib3N0aW5nKHRoaXMpOwogICAgICAgIGxldCBkb20gPSBzdXBlci5fc3RhbXBUZW1wbGF0ZSh0ZW1wbGF0ZSk7CiAgICAgICAgaG9zdFN0YWNrLmVuZEhvc3RpbmcodGhpcyk7CiAgICAgICAgbGV0IHRlbXBsYXRlSW5mbyA9IC8qKiBAdHlwZSB7IVRlbXBsYXRlSW5mb30gKi8odGhpcy5fYmluZFRlbXBsYXRlKHRlbXBsYXRlLCB0cnVlKSk7CiAgICAgICAgLy8gQWRkIHRlbXBsYXRlLWluc3RhbmNlLXNwZWNpZmljIGRhdGEgdG8gaW5zdGFuY2VkIHRlbXBsYXRlSW5mbwogICAgICAgIHRlbXBsYXRlSW5mby5ub2RlTGlzdCA9IGRvbS5ub2RlTGlzdDsKICAgICAgICAvLyBDYXB0dXJlIGNoaWxkIG5vZGVzIHRvIGFsbG93IHVuc3RhbXBpbmcgb2Ygbm9uLXByb3RvdHlwaWNhbCB0ZW1wbGF0ZXMKICAgICAgICBpZiAoIXRlbXBsYXRlSW5mby53YXNQcmVCb3VuZCkgewogICAgICAgICAgbGV0IG5vZGVzID0gdGVtcGxhdGVJbmZvLmNoaWxkTm9kZXMgPSBbXTsKICAgICAgICAgIGZvciAobGV0IG49ZG9tLmZpcnN0Q2hpbGQ7IG47IG49bi5uZXh0U2libGluZykgewogICAgICAgICAgICBub2Rlcy5wdXNoKG4pOwogICAgICAgICAgfQogICAgICAgIH0KICAgICAgICBkb20udGVtcGxhdGVJbmZvID0gdGVtcGxhdGVJbmZvOwogICAgICAgIC8vIFNldHVwIGNvbXBvdW5kIHN0b3JhZ2UsIDItd2F5IGxpc3RlbmVycywgYW5kIGRhdGFIb3N0IGZvciBiaW5kaW5ncwogICAgICAgIHNldHVwQmluZGluZ3ModGhpcywgdGVtcGxhdGVJbmZvKTsKICAgICAgICAvLyBGbHVzaCBwcm9wZXJ0aWVzIGludG8gdGVtcGxhdGUgbm9kZXMgaWYgYWxyZWFkeSBib290ZWQKICAgICAgICBpZiAodGhpcy5fX2RhdGFSZWFkeSkgewogICAgICAgICAgcnVuRWZmZWN0cyh0aGlzLCB0ZW1wbGF0ZUluZm8ucHJvcGVydHlFZmZlY3RzLCB0aGlzLl9fZGF0YSwgbnVsbCwKICAgICAgICAgICAgZmFsc2UsIHRlbXBsYXRlSW5mby5ub2RlTGlzdCk7CiAgICAgICAgfQogICAgICAgIHJldHVybiBkb207CiAgICAgIH0KCiAgICAgIC8qKgogICAgICAgKiBSZW1vdmVzIGFuZCB1bmJpbmRzIHRoZSBub2RlcyBwcmV2aW91c2x5IGNvbnRhaW5lZCBpbiB0aGUgcHJvdmlkZWQKICAgICAgICogRG9jdW1lbnRGcmFnbWVudCByZXR1cm5lZCBmcm9tIGBfc3RhbXBUZW1wbGF0ZWAuCiAgICAgICAqCiAgICAgICAqIEBwYXJhbSB7IVN0YW1wZWRUZW1wbGF0ZX0gZG9tIERvY3VtZW50RnJhZ21lbnQgcHJldmlvdXNseSByZXR1cm5lZAogICAgICAgKiAgIGZyb20gYF9zdGFtcFRlbXBsYXRlYCBhc3NvY2lhdGVkIHdpdGggdGhlIG5vZGVzIHRvIGJlIHJlbW92ZWQKICAgICAgICogQHJldHVybiB7dm9pZH0KICAgICAgICogQHByb3RlY3RlZAogICAgICAgKi8KICAgICAgX3JlbW92ZUJvdW5kRG9tKGRvbSkgewogICAgICAgIC8vIFVubGluayB0ZW1wbGF0ZSBpbmZvCiAgICAgICAgbGV0IHRlbXBsYXRlSW5mbyA9IGRvbS50ZW1wbGF0ZUluZm87CiAgICAgICAgaWYgKHRlbXBsYXRlSW5mby5wcmV2aW91c1RlbXBsYXRlSW5mbykgewogICAgICAgICAgdGVtcGxhdGVJbmZvLnByZXZpb3VzVGVtcGxhdGVJbmZvLm5leHRUZW1wbGF0ZUluZm8gPQogICAgICAgICAgICB0ZW1wbGF0ZUluZm8ubmV4dFRlbXBsYXRlSW5mbzsKICAgICAgICB9CiAgICAgICAgaWYgKHRlbXBsYXRlSW5mby5uZXh0VGVtcGxhdGVJbmZvKSB7CiAgICAgICAgICB0ZW1wbGF0ZUluZm8ubmV4dFRlbXBsYXRlSW5mby5wcmV2aW91c1RlbXBsYXRlSW5mbyA9CiAgICAgICAgICAgIHRlbXBsYXRlSW5mby5wcmV2aW91c1RlbXBsYXRlSW5mbzsKICAgICAgICB9CiAgICAgICAgaWYgKHRoaXMuX190ZW1wbGF0ZUluZm9MYXN0ID09IHRlbXBsYXRlSW5mbykgewogICAgICAgICAgdGhpcy5fX3RlbXBsYXRlSW5mb0xhc3QgPSB0ZW1wbGF0ZUluZm8ucHJldmlvdXNUZW1wbGF0ZUluZm87CiAgICAgICAgfQogICAgICAgIHRlbXBsYXRlSW5mby5wcmV2aW91c1RlbXBsYXRlSW5mbyA9IHRlbXBsYXRlSW5mby5uZXh0VGVtcGxhdGVJbmZvID0gbnVsbDsKICAgICAgICAvLyBSZW1vdmUgc3RhbXBlZCBub2RlcwogICAgICAgIGxldCBub2RlcyA9IHRlbXBsYXRlSW5mby5jaGlsZE5vZGVzOwogICAgICAgIGZvciAobGV0IGk9MDsgaTxub2Rlcy5sZW5ndGg7IGkrKykgewogICAgICAgICAgbGV0IG5vZGUgPSBub2Rlc1tpXTsKICAgICAgICAgIG5vZGUucGFyZW50Tm9kZS5yZW1vdmVDaGlsZChub2RlKTsKICAgICAgICB9CiAgICAgIH0KCiAgICAgIC8qKgogICAgICAgKiBPdmVycmlkZXMgZGVmYXVsdCBgVGVtcGxhdGVTdGFtcGAgaW1wbGVtZW50YXRpb24gdG8gYWRkIHN1cHBvcnQgZm9yCiAgICAgICAqIHBhcnNpbmcgYmluZGluZ3MgZnJvbSBgVGV4dE5vZGVgJ3MnIGB0ZXh0Q29udGVudGAuICBBIGBiaW5kaW5nc2AKICAgICAgICogYXJyYXkgaXMgYWRkZWQgdG8gYG5vZGVJbmZvYCBhbmQgcG9wdWxhdGVkIHdpdGggYmluZGluZyBtZXRhZGF0YQogICAgICAgKiB3aXRoIGluZm9ybWF0aW9uIGNhcHR1cmluZyB0aGUgYmluZGluZyB0YXJnZXQsIGFuZCBhIGBwYXJ0c2AgYXJyYXkKICAgICAgICogd2l0aCBvbmUgb3IgbW9yZSBtZXRhZGF0YSBvYmplY3RzIGNhcHR1cmluZyB0aGUgc291cmNlKHMpIG9mIHRoZQogICAgICAgKiBiaW5kaW5nLgogICAgICAgKgogICAgICAgKiBAb3ZlcnJpZGUKICAgICAgICogQHBhcmFtIHtOb2RlfSBub2RlIE5vZGUgdG8gcGFyc2UKICAgICAgICogQHBhcmFtIHtUZW1wbGF0ZUluZm99IHRlbXBsYXRlSW5mbyBUZW1wbGF0ZSBtZXRhZGF0YSBmb3IgY3VycmVudCB0ZW1wbGF0ZQogICAgICAgKiBAcGFyYW0ge05vZGVJbmZvfSBub2RlSW5mbyBOb2RlIG1ldGFkYXRhIGZvciBjdXJyZW50IHRlbXBsYXRlIG5vZGUKICAgICAgICogQHJldHVybiB7Ym9vbGVhbn0gYHRydWVgIGlmIHRoZSB2aXNpdGVkIG5vZGUgYWRkZWQgbm9kZS1zcGVjaWZpYwogICAgICAgKiAgIG1ldGFkYXRhIHRvIGBub2RlSW5mb2AKICAgICAgICogQHByb3RlY3RlZAogICAgICAgKiBAc3VwcHJlc3Mge21pc3NpbmdQcm9wZXJ0aWVzfSBJbnRlcmZhY2VzIGluIGNsb3N1cmUgZG8gbm90IGluaGVyaXQgc3RhdGljcywgYnV0IGNsYXNzZXMgZG8KICAgICAgICovCiAgICAgIHN0YXRpYyBfcGFyc2VUZW1wbGF0ZU5vZGUobm9kZSwgdGVtcGxhdGVJbmZvLCBub2RlSW5mbykgewogICAgICAgIGxldCBub3RlZCA9IHN1cGVyLl9wYXJzZVRlbXBsYXRlTm9kZShub2RlLCB0ZW1wbGF0ZUluZm8sIG5vZGVJbmZvKTsKICAgICAgICBpZiAobm9kZS5ub2RlVHlwZSA9PT0gTm9kZS5URVhUX05PREUpIHsKICAgICAgICAgIGxldCBwYXJ0cyA9IHRoaXMuX3BhcnNlQmluZGluZ3Mobm9kZS50ZXh0Q29udGVudCwgdGVtcGxhdGVJbmZvKTsKICAgICAgICAgIGlmIChwYXJ0cykgewogICAgICAgICAgICAvLyBJbml0aWFsaXplIHRoZSB0ZXh0Q29udGVudCB3aXRoIGFueSBsaXRlcmFsIHBhcnRzCiAgICAgICAgICAgIC8vIE5PVEU6IGRlZmF1bHQgdG8gYSBzcGFjZSBoZXJlIHNvIHRoZSB0ZXh0Tm9kZSByZW1haW5zOyBzb21lIGJyb3dzZXJzCiAgICAgICAgICAgIC8vIChJRSkgb21pdCBhbiBlbXB0eSB0ZXh0Tm9kZSBmb2xsb3dpbmcgY2xvbmVOb2RlL2ltcG9ydE5vZGUuCiAgICAgICAgICAgIG5vZGUudGV4dENvbnRlbnQgPSBsaXRlcmFsRnJvbVBhcnRzKHBhcnRzKSB8fCAnICc7CiAgICAgICAgICAgIGFkZEJpbmRpbmcodGhpcywgdGVtcGxhdGVJbmZvLCBub2RlSW5mbywgJ3RleHQnLCAndGV4dENvbnRlbnQnLCBwYXJ0cyk7CiAgICAgICAgICAgIG5vdGVkID0gdHJ1ZTsKICAgICAgICAgIH0KICAgICAgICB9CiAgICAgICAgcmV0dXJuIG5vdGVkOwogICAgICB9CgogICAgICAvKioKICAgICAgICogT3ZlcnJpZGVzIGRlZmF1bHQgYFRlbXBsYXRlU3RhbXBgIGltcGxlbWVudGF0aW9uIHRvIGFkZCBzdXBwb3J0IGZvcgogICAgICAgKiBwYXJzaW5nIGJpbmRpbmdzIGZyb20gYXR0cmlidXRlcy4gIEEgYGJpbmRpbmdzYAogICAgICAgKiBhcnJheSBpcyBhZGRlZCB0byBgbm9kZUluZm9gIGFuZCBwb3B1bGF0ZWQgd2l0aCBiaW5kaW5nIG1ldGFkYXRhCiAgICAgICAqIHdpdGggaW5mb3JtYXRpb24gY2FwdHVyaW5nIHRoZSBiaW5kaW5nIHRhcmdldCwgYW5kIGEgYHBhcnRzYCBhcnJheQogICAgICAgKiB3aXRoIG9uZSBvciBtb3JlIG1ldGFkYXRhIG9iamVjdHMgY2FwdHVyaW5nIHRoZSBzb3VyY2Uocykgb2YgdGhlCiAgICAgICAqIGJpbmRpbmcuCiAgICAgICAqCiAgICAgICAqIEBvdmVycmlkZQogICAgICAgKiBAcGFyYW0ge0VsZW1lbnR9IG5vZGUgTm9kZSB0byBwYXJzZQogICAgICAgKiBAcGFyYW0ge1RlbXBsYXRlSW5mb30gdGVtcGxhdGVJbmZvIFRlbXBsYXRlIG1ldGFkYXRhIGZvciBjdXJyZW50IHRlbXBsYXRlCiAgICAgICAqIEBwYXJhbSB7Tm9kZUluZm99IG5vZGVJbmZvIE5vZGUgbWV0YWRhdGEgZm9yIGN1cnJlbnQgdGVtcGxhdGUgbm9kZQogICAgICAgKiBAcGFyYW0ge3N0cmluZ30gbmFtZSBBdHRyaWJ1dGUgbmFtZQogICAgICAgKiBAcGFyYW0ge3N0cmluZ30gdmFsdWUgQXR0cmlidXRlIHZhbHVlCiAgICAgICAqIEByZXR1cm4ge2Jvb2xlYW59IGB0cnVlYCBpZiB0aGUgdmlzaXRlZCBub2RlIGFkZGVkIG5vZGUtc3BlY2lmaWMKICAgICAgICogICBtZXRhZGF0YSB0byBgbm9kZUluZm9gCiAgICAgICAqIEBwcm90ZWN0ZWQKICAgICAgICogQHN1cHByZXNzIHttaXNzaW5nUHJvcGVydGllc30gSW50ZXJmYWNlcyBpbiBjbG9zdXJlIGRvIG5vdCBpbmhlcml0IHN0YXRpY3MsIGJ1dCBjbGFzc2VzIGRvCiAgICAgICAqLwogICAgICBzdGF0aWMgX3BhcnNlVGVtcGxhdGVOb2RlQXR0cmlidXRlKG5vZGUsIHRlbXBsYXRlSW5mbywgbm9kZUluZm8sIG5hbWUsIHZhbHVlKSB7CiAgICAgICAgbGV0IHBhcnRzID0gdGhpcy5fcGFyc2VCaW5kaW5ncyh2YWx1ZSwgdGVtcGxhdGVJbmZvKTsKICAgICAgICBpZiAocGFydHMpIHsKICAgICAgICAgIC8vIEF0dHJpYnV0ZSBvciBwcm9wZXJ0eQogICAgICAgICAgbGV0IG9yaWdOYW1lID0gbmFtZTsKICAgICAgICAgIGxldCBraW5kID0gJ3Byb3BlcnR5JzsKICAgICAgICAgIC8vIFRoZSBvbmx5IHdheSB3ZSBzZWUgYSBjYXBpdGFsIGxldHRlciBoZXJlIGlzIGlmIHRoZSBhdHRyIGhhcwogICAgICAgICAgLy8gYSBjYXBpdGFsIGxldHRlciBpbiBpdCBwZXIgc3BlYy4gSW4gdGhpcyBjYXNlLCB0byBtYWtlIHN1cmUKICAgICAgICAgIC8vIHRoaXMgYmluZGluZyB3b3Jrcywgd2UgZ28gYWhlYWQgYW5kIG1ha2UgdGhlIGJpbmRpbmcgdG8gdGhlIGF0dHJpYnV0ZS4KICAgICAgICAgIGlmIChjYXBpdGFsQXR0cmlidXRlUmVnZXgudGVzdChuYW1lKSkgewogICAgICAgICAgICBraW5kID0gJ2F0dHJpYnV0ZSc7CiAgICAgICAgICB9IGVsc2UgaWYgKG5hbWVbbmFtZS5sZW5ndGgtMV0gPT0gJyQnKSB7CiAgICAgICAgICAgIG5hbWUgPSBuYW1lLnNsaWNlKDAsIC0xKTsKICAgICAgICAgICAga2luZCA9ICdhdHRyaWJ1dGUnOwogICAgICAgICAgfQogICAgICAgICAgLy8gSW5pdGlhbGl6ZSBhdHRyaWJ1dGUgYmluZGluZ3Mgd2l0aCBhbnkgbGl0ZXJhbCBwYXJ0cwogICAgICAgICAgbGV0IGxpdGVyYWwgPSBsaXRlcmFsRnJvbVBhcnRzKHBhcnRzKTsKICAgICAgICAgIGlmIChsaXRlcmFsICYmIGtpbmQgPT0gJ2F0dHJpYnV0ZScpIHsKICAgICAgICAgICAgLy8gRW5zdXJlIGEgU2hhZHlDU1MgdGVtcGxhdGUgc2NvcGVkIHN0eWxlIGlzIG5vdCByZW1vdmVkCiAgICAgICAgICAgIC8vIHdoZW4gYSBjbGFzcyQgYmluZGluZydzIGluaXRpYWwgbGl0ZXJhbCB2YWx1ZSBpcyBzZXQuCiAgICAgICAgICAgIGlmIChuYW1lID09ICdjbGFzcycgJiYgbm9kZS5oYXNBdHRyaWJ1dGUoJ2NsYXNzJykpIHsKICAgICAgICAgICAgICBsaXRlcmFsICs9ICcgJyArIG5vZGUuZ2V0QXR0cmlidXRlKG5hbWUpOwogICAgICAgICAgICB9CiAgICAgICAgICAgIG5vZGUuc2V0QXR0cmlidXRlKG5hbWUsIGxpdGVyYWwpOwogICAgICAgICAgfQogICAgICAgICAgLy8gQ2xlYXIgYXR0cmlidXRlIGJlZm9yZSByZW1vdmluZywgc2luY2UgSUUgd29uJ3QgYWxsb3cgcmVtb3ZpbmcKICAgICAgICAgIC8vIGB2YWx1ZWAgYXR0cmlidXRlIGlmIGl0IHByZXZpb3VzbHkgaGFkIGEgdmFsdWUgKGNhbid0CiAgICAgICAgICAvLyB1bmNvbmRpdGlvbmFsbHkgc2V0ICcnIGJlZm9yZSByZW1vdmluZyBzaW5jZSBhdHRyaWJ1dGVzIHdpdGggYCRgCiAgICAgICAgICAvLyBjYW4ndCBiZSBzZXQgdXNpbmcgc2V0QXR0cmlidXRlKQogICAgICAgICAgaWYgKG5vZGUubG9jYWxOYW1lID09PSAnaW5wdXQnICYmIG9yaWdOYW1lID09PSAndmFsdWUnKSB7CiAgICAgICAgICAgIG5vZGUuc2V0QXR0cmlidXRlKG9yaWdOYW1lLCAnJyk7CiAgICAgICAgICB9CiAgICAgICAgICAvLyBSZW1vdmUgYW5ub3RhdGlvbgogICAgICAgICAgbm9kZS5yZW1vdmVBdHRyaWJ1dGUob3JpZ05hbWUpOwogICAgICAgICAgLy8gQ2FzZSBoYWNrZXJ5OiBhdHRyaWJ1dGVzIGFyZSBsb3dlci1jYXNlLCBidXQgYmluZCB0YXJnZXRzCiAgICAgICAgICAvLyAocHJvcGVydGllcykgYXJlIGNhc2Ugc2Vuc2l0aXZlLiBHYW1iaXQgaXMgdG8gbWFwIGRhc2gtY2FzZSB0bwogICAgICAgICAgLy8gY2FtZWwtY2FzZTogYGZvby1iYXJgIGJlY29tZXMgYGZvb0JhcmAuCiAgICAgICAgICAvLyBBdHRyaWJ1dGUgYmluZGluZ3MgYXJlIGV4Y2VwdGVkLgogICAgICAgICAgaWYgKGtpbmQgPT09ICdwcm9wZXJ0eScpIHsKICAgICAgICAgICAgbmFtZSA9IFBvbHltZXIuQ2FzZU1hcC5kYXNoVG9DYW1lbENhc2UobmFtZSk7CiAgICAgICAgICB9CiAgICAgICAgICBhZGRCaW5kaW5nKHRoaXMsIHRlbXBsYXRlSW5mbywgbm9kZUluZm8sIGtpbmQsIG5hbWUsIHBhcnRzLCBsaXRlcmFsKTsKICAgICAgICAgIHJldHVybiB0cnVlOwogICAgICAgIH0gZWxzZSB7CiAgICAgICAgICByZXR1cm4gc3VwZXIuX3BhcnNlVGVtcGxhdGVOb2RlQXR0cmlidXRlKG5vZGUsIHRlbXBsYXRlSW5mbywgbm9kZUluZm8sIG5hbWUsIHZhbHVlKTsKICAgICAgICB9CiAgICAgIH0KCiAgICAgIC8qKgogICAgICAgKiBPdmVycmlkZXMgZGVmYXVsdCBgVGVtcGxhdGVTdGFtcGAgaW1wbGVtZW50YXRpb24gdG8gYWRkIHN1cHBvcnQgZm9yCiAgICAgICAqIGJpbmRpbmcgdGhlIHByb3BlcnRpZXMgdGhhdCBhIG5lc3RlZCB0ZW1wbGF0ZSBkZXBlbmRzIG9uIHRvIHRoZSB0ZW1wbGF0ZQogICAgICAgKiBhcyBgX2hvc3RfPHByb3BlcnR5PmAuCiAgICAgICAqCiAgICAgICAqIEBvdmVycmlkZQogICAgICAgKiBAcGFyYW0ge05vZGV9IG5vZGUgTm9kZSB0byBwYXJzZQogICAgICAgKiBAcGFyYW0ge1RlbXBsYXRlSW5mb30gdGVtcGxhdGVJbmZvIFRlbXBsYXRlIG1ldGFkYXRhIGZvciBjdXJyZW50IHRlbXBsYXRlCiAgICAgICAqIEBwYXJhbSB7Tm9kZUluZm99IG5vZGVJbmZvIE5vZGUgbWV0YWRhdGEgZm9yIGN1cnJlbnQgdGVtcGxhdGUgbm9kZQogICAgICAgKiBAcmV0dXJuIHtib29sZWFufSBgdHJ1ZWAgaWYgdGhlIHZpc2l0ZWQgbm9kZSBhZGRlZCBub2RlLXNwZWNpZmljCiAgICAgICAqICAgbWV0YWRhdGEgdG8gYG5vZGVJbmZvYAogICAgICAgKiBAcHJvdGVjdGVkCiAgICAgICAqIEBzdXBwcmVzcyB7bWlzc2luZ1Byb3BlcnRpZXN9IEludGVyZmFjZXMgaW4gY2xvc3VyZSBkbyBub3QgaW5oZXJpdCBzdGF0aWNzLCBidXQgY2xhc3NlcyBkbwogICAgICAgKi8KICAgICAgc3RhdGljIF9wYXJzZVRlbXBsYXRlTmVzdGVkVGVtcGxhdGUobm9kZSwgdGVtcGxhdGVJbmZvLCBub2RlSW5mbykgewogICAgICAgIGxldCBub3RlZCA9IHN1cGVyLl9wYXJzZVRlbXBsYXRlTmVzdGVkVGVtcGxhdGUobm9kZSwgdGVtcGxhdGVJbmZvLCBub2RlSW5mbyk7CiAgICAgICAgLy8gTWVyZ2UgaG9zdCBwcm9wcyBpbnRvIG91dGVyIHRlbXBsYXRlIGFuZCBhZGQgYmluZGluZ3MKICAgICAgICBsZXQgaG9zdFByb3BzID0gbm9kZUluZm8udGVtcGxhdGVJbmZvLmhvc3RQcm9wczsKICAgICAgICBsZXQgbW9kZSA9ICd7JzsKICAgICAgICBmb3IgKGxldCBzb3VyY2UgaW4gaG9zdFByb3BzKSB7CiAgICAgICAgICBsZXQgcGFydHMgPSBbeyBtb2RlLCBzb3VyY2UsIGRlcGVuZGVuY2llczogW3NvdXJjZV0gfV07CiAgICAgICAgICBhZGRCaW5kaW5nKHRoaXMsIHRlbXBsYXRlSW5mbywgbm9kZUluZm8sICdwcm9wZXJ0eScsICdfaG9zdF8nICsgc291cmNlLCBwYXJ0cyk7CiAgICAgICAgfQogICAgICAgIHJldHVybiBub3RlZDsKICAgICAgfQoKICAgICAgLyoqCiAgICAgICAqIENhbGxlZCB0byBwYXJzZSB0ZXh0IGluIGEgdGVtcGxhdGUgKGVpdGhlciBhdHRyaWJ1dGUgdmFsdWVzIG9yCiAgICAgICAqIHRleHRDb250ZW50KSBpbnRvIGJpbmRpbmcgbWV0YWRhdGEuCiAgICAgICAqCiAgICAgICAqIEFueSBvdmVycmlkZXMgb2YgdGhpcyBtZXRob2Qgc2hvdWxkIHJldHVybiBhbiBhcnJheSBvZiBiaW5kaW5nIHBhcnQKICAgICAgICogbWV0YWRhdGEgIHJlcHJlc2VudGluZyBvbmUgb3IgbW9yZSBiaW5kaW5ncyBmb3VuZCBpbiB0aGUgcHJvdmlkZWQgdGV4dAogICAgICAgKiBhbmQgYW55ICJsaXRlcmFsIiB0ZXh0IGluIGJldHdlZW4uICBBbnkgbm9uLWxpdGVyYWwgcGFydHMgd2lsbCBiZSBwYXNzZWQKICAgICAgICogdG8gYF9ldmFsdWF0ZUJpbmRpbmdgIHdoZW4gYW55IGRlcGVuZGVuY2llcyBjaGFuZ2UuICBUaGUgb25seSByZXF1aXJlZAogICAgICAgKiBmaWVsZHMgb2YgZWFjaCAicGFydCIgaW4gdGhlIHJldHVybmVkIGFycmF5IGFyZSBhcyBmb2xsb3dzOgogICAgICAgKgogICAgICAgKiAtIGBkZXBlbmRlbmNpZXNgIC0gQXJyYXkgY29udGFpbmluZyB0cmlnZ2VyIG1ldGFkYXRhIGZvciBlYWNoIHByb3BlcnR5CiAgICAgICAqICAgdGhhdCBzaG91bGQgdHJpZ2dlciB0aGUgYmluZGluZyB0byB1cGRhdGUKICAgICAgICogLSBgbGl0ZXJhbGAgLSBTdHJpbmcgY29udGFpbmluZyB0ZXh0IGlmIHRoZSBwYXJ0IHJlcHJlc2VudHMgYSBsaXRlcmFsOwogICAgICAgKiAgIGluIHRoaXMgY2FzZSBubyBgZGVwZW5kZW5jaWVzYCBhcmUgbmVlZGVkCiAgICAgICAqCiAgICAgICAqIEFkZGl0aW9uYWwgbWV0YWRhdGEgZm9yIHVzZSBieSBgX2V2YWx1YXRlQmluZGluZ2AgbWF5IGJlIHByb3ZpZGVkIGluCiAgICAgICAqIGVhY2ggcGFydCBvYmplY3QgYXMgbmVlZGVkLgogICAgICAgKgogICAgICAgKiBUaGUgZGVmYXVsdCBpbXBsZW1lbnRhdGlvbiBoYW5kbGVzIHRoZSBmb2xsb3dpbmcgdHlwZXMgb2YgYmluZGluZ3MKICAgICAgICogKG9uZSBvciBtb3JlIG1heSBiZSBpbnRlcm1peGVkIHdpdGggbGl0ZXJhbCBzdHJpbmdzKToKICAgICAgICogLSBQcm9wZXJ0eSBiaW5kaW5nOiBgW1twcm9wXV1gCiAgICAgICAqIC0gUGF0aCBiaW5kaW5nOiBgW1tvYmplY3QucHJvcF1dYAogICAgICAgKiAtIE5lZ2F0ZWQgcHJvcGVydHkgb3IgcGF0aCBiaW5kaW5nczogYFtbIXByb3BdXWAgb3IgYFtbIW9iamVjdC5wcm9wXV1gCiAgICAgICAqIC0gVHdvLXdheSBwcm9wZXJ0eSBvciBwYXRoIGJpbmRpbmdzIChzdXBwb3J0cyBuZWdhdGlvbik6CiAgICAgICAqICAgYHt7cHJvcH19YCwgYHt7b2JqZWN0LnByb3B9fWAsIGB7eyFwcm9wfX1gIG9yIGB7eyFvYmplY3QucHJvcH19YAogICAgICAgKiAtIElubGluZSBjb21wdXRlZCBtZXRob2QgKHN1cHBvcnRzIG5lZ2F0aW9uKToKICAgICAgICogICBgW1tjb21wdXRlKGEsICdsaXRlcmFsJywgYildXWAsIGBbWyFjb21wdXRlKGEsICdsaXRlcmFsJywgYildXWAKICAgICAgICoKICAgICAgICogVGhlIGRlZmF1bHQgaW1wbGVtZW50YXRpb24gdXNlcyBhIHJlZ3VsYXIgZXhwcmVzc2lvbiBmb3IgYmVzdAogICAgICAgKiBwZXJmb3JtYW5jZS4gSG93ZXZlciwgdGhlIHJlZ3VsYXIgZXhwcmVzc2lvbiB1c2VzIGEgd2hpdGUtbGlzdCBvZgogICAgICAgKiBhbGxvd2VkIGNoYXJhY3RlcnMgaW4gYSBkYXRhLWJpbmRpbmcsIHdoaWNoIGNhdXNlcyBwcm9ibGVtcyBmb3IKICAgICAgICogZGF0YS1iaW5kaW5ncyB0aGF0IGRvIHVzZSBjaGFyYWN0ZXJzIG5vdCBpbiB0aGlzIHdoaXRlLWxpc3QuCiAgICAgICAqCiAgICAgICAqIEluc3RlYWQgb2YgdXBkYXRpbmcgdGhlIHdoaXRlLWxpc3Qgd2l0aCBhbGwgYWxsb3dlZCBjaGFyYWN0ZXJzLAogICAgICAgKiB0aGVyZSBpcyBhIFN0cmljdEJpbmRpbmdQYXJzZXIgKHNlZSBsaWIvbWl4aW5zL3N0cmljdC1iaW5kaW5nLXBhcnNlcikKICAgICAgICogdGhhdCB1c2VzIGEgc3RhdGUgbWFjaGluZSBpbnN0ZWFkLiBUaGlzIHN0YXRlIG1hY2hpbmUgaXMgYWJsZSB0byBoYW5kbGUKICAgICAgICogYWxsIGNoYXJhY3RlcnMuIEhvd2V2ZXIsIGl0IGlzIHNsaWdodGx5IGxlc3MgcGVyZm9ybWFudCwgdGhlcmVmb3JlIHdlCiAgICAgICAqIGV4dHJhY3RlZCBpdCBpbnRvIGEgc2VwYXJhdGUgb3B0aW9uYWwgbWl4aW4uCiAgICAgICAqCiAgICAgICAqIEBwYXJhbSB7c3RyaW5nfSB0ZXh0IFRleHQgdG8gcGFyc2UgZnJvbSBhdHRyaWJ1dGUgb3IgdGV4dENvbnRlbnQKICAgICAgICogQHBhcmFtIHtPYmplY3R9IHRlbXBsYXRlSW5mbyBDdXJyZW50IHRlbXBsYXRlIG1ldGFkYXRhCiAgICAgICAqIEByZXR1cm4ge0FycmF5PCFCaW5kaW5nUGFydD59IEFycmF5IG9mIGJpbmRpbmcgcGFydCBtZXRhZGF0YQogICAgICAgKiBAcHJvdGVjdGVkCiAgICAgICAqLwogICAgICBzdGF0aWMgX3BhcnNlQmluZGluZ3ModGV4dCwgdGVtcGxhdGVJbmZvKSB7CiAgICAgICAgbGV0IHBhcnRzID0gW107CiAgICAgICAgbGV0IGxhc3RJbmRleCA9IDA7CiAgICAgICAgbGV0IG07CiAgICAgICAgLy8gRXhhbXBsZTogImxpdGVyYWwxe3twcm9wfX1saXRlcmFsMltbIWNvbXB1dGUoZm9vLGJhcildXWZpbmFsIgogICAgICAgIC8vIFJlZ2V4IG1hdGNoZXM6CiAgICAgICAgLy8gICAgICAgIEl0ZXJhdGlvbiAxOiAgSXRlcmF0aW9uIDI6CiAgICAgICAgLy8gbVsxXTogJ3t7JyAgICAgICAgICAnW1snCiAgICAgICAgLy8gbVsyXTogJycgICAgICAgICAgICAnIScKICAgICAgICAvLyBtWzNdOiAncHJvcCcgICAgICAgICdjb21wdXRlKGZvbyxiYXIpJwogICAgICAgIHdoaWxlICgobSA9IGJpbmRpbmdSZWdleC5leGVjKHRleHQpKSAhPT0gbnVsbCkgewogICAgICAgICAgLy8gQWRkIGxpdGVyYWwgcGFydAogICAgICAgICAgaWYgKG0uaW5kZXggPiBsYXN0SW5kZXgpIHsKICAgICAgICAgICAgcGFydHMucHVzaCh7bGl0ZXJhbDogdGV4dC5zbGljZShsYXN0SW5kZXgsIG0uaW5kZXgpfSk7CiAgICAgICAgICB9CiAgICAgICAgICAvLyBBZGQgYmluZGluZyBwYXJ0CiAgICAgICAgICBsZXQgbW9kZSA9IG1bMV1bMF07CiAgICAgICAgICBsZXQgbmVnYXRlID0gQm9vbGVhbihtWzJdKTsKICAgICAgICAgIGxldCBzb3VyY2UgPSBtWzNdLnRyaW0oKTsKICAgICAgICAgIGxldCBjdXN0b21FdmVudCA9IGZhbHNlLCBub3RpZnlFdmVudCA9ICcnLCBjb2xvbiA9IC0xOwogICAgICAgICAgaWYgKG1vZGUgPT0gJ3snICYmIChjb2xvbiA9IHNvdXJjZS5pbmRleE9mKCc6OicpKSA+IDApIHsKICAgICAgICAgICAgbm90aWZ5RXZlbnQgPSBzb3VyY2Uuc3Vic3RyaW5nKGNvbG9uICsgMik7CiAgICAgICAgICAgIHNvdXJjZSA9IHNvdXJjZS5zdWJzdHJpbmcoMCwgY29sb24pOwogICAgICAgICAgICBjdXN0b21FdmVudCA9IHRydWU7CiAgICAgICAgICB9CiAgICAgICAgICBsZXQgc2lnbmF0dXJlID0gcGFyc2VNZXRob2Qoc291cmNlKTsKICAgICAgICAgIGxldCBkZXBlbmRlbmNpZXMgPSBbXTsKICAgICAgICAgIGlmIChzaWduYXR1cmUpIHsKICAgICAgICAgICAgLy8gSW5saW5lIGNvbXB1dGVkIGZ1bmN0aW9uCiAgICAgICAgICAgIGxldCB7YXJncywgbWV0aG9kTmFtZX0gPSBzaWduYXR1cmU7CiAgICAgICAgICAgIGZvciAobGV0IGk9MDsgaTxhcmdzLmxlbmd0aDsgaSsrKSB7CiAgICAgICAgICAgICAgbGV0IGFyZyA9IGFyZ3NbaV07CiAgICAgICAgICAgICAgaWYgKCFhcmcubGl0ZXJhbCkgewogICAgICAgICAgICAgICAgZGVwZW5kZW5jaWVzLnB1c2goYXJnKTsKICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICAgICAgbGV0IGR5bmFtaWNGbnMgPSB0ZW1wbGF0ZUluZm8uZHluYW1pY0ZuczsKICAgICAgICAgICAgaWYgKGR5bmFtaWNGbnMgJiYgZHluYW1pY0Zuc1ttZXRob2ROYW1lXSB8fCBzaWduYXR1cmUuc3RhdGljKSB7CiAgICAgICAgICAgICAgZGVwZW5kZW5jaWVzLnB1c2gobWV0aG9kTmFtZSk7CiAgICAgICAgICAgICAgc2lnbmF0dXJlLmR5bmFtaWNGbiA9IHRydWU7CiAgICAgICAgICAgIH0KICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgIC8vIFByb3BlcnR5IG9yIHBhdGgKICAgICAgICAgICAgZGVwZW5kZW5jaWVzLnB1c2goc291cmNlKTsKICAgICAgICAgIH0KICAgICAgICAgIHBhcnRzLnB1c2goewogICAgICAgICAgICBzb3VyY2UsIG1vZGUsIG5lZ2F0ZSwgY3VzdG9tRXZlbnQsIHNpZ25hdHVyZSwgZGVwZW5kZW5jaWVzLAogICAgICAgICAgICBldmVudDogbm90aWZ5RXZlbnQKICAgICAgICAgIH0pOwogICAgICAgICAgbGFzdEluZGV4ID0gYmluZGluZ1JlZ2V4Lmxhc3RJbmRleDsKICAgICAgICB9CiAgICAgICAgLy8gQWRkIGEgZmluYWwgbGl0ZXJhbCBwYXJ0CiAgICAgICAgaWYgKGxhc3RJbmRleCAmJiBsYXN0SW5kZXggPCB0ZXh0Lmxlbmd0aCkgewogICAgICAgICAgbGV0IGxpdGVyYWwgPSB0ZXh0LnN1YnN0cmluZyhsYXN0SW5kZXgpOwogICAgICAgICAgaWYgKGxpdGVyYWwpIHsKICAgICAgICAgICAgcGFydHMucHVzaCh7CiAgICAgICAgICAgICAgbGl0ZXJhbDogbGl0ZXJhbAogICAgICAgICAgICB9KTsKICAgICAgICAgIH0KICAgICAgICB9CiAgICAgICAgaWYgKHBhcnRzLmxlbmd0aCkgewogICAgICAgICAgcmV0dXJuIHBhcnRzOwogICAgICAgIH0gZWxzZSB7CiAgICAgICAgICByZXR1cm4gbnVsbDsKICAgICAgICB9CiAgICAgIH0KCiAgICAgIC8qKgogICAgICAgKiBDYWxsZWQgdG8gZXZhbHVhdGUgYSBwcmV2aW91c2x5IHBhcnNlZCBiaW5kaW5nIHBhcnQgYmFzZWQgb24gYSBzZXQgb2YKICAgICAgICogb25lIG9yIG1vcmUgY2hhbmdlZCBkZXBlbmRlbmNpZXMuCiAgICAgICAqCiAgICAgICAqIEBwYXJhbSB7dGhpc30gaW5zdCBFbGVtZW50IHRoYXQgc2hvdWxkIGJlIHVzZWQgYXMgc2NvcGUgZm9yCiAgICAgICAqICAgYmluZGluZyBkZXBlbmRlbmNpZXMKICAgICAgICogQHBhcmFtIHtCaW5kaW5nUGFydH0gcGFydCBCaW5kaW5nIHBhcnQgbWV0YWRhdGEKICAgICAgICogQHBhcmFtIHtzdHJpbmd9IHBhdGggUHJvcGVydHkvcGF0aCB0aGF0IHRyaWdnZXJlZCB0aGlzIGVmZmVjdAogICAgICAgKiBAcGFyYW0ge09iamVjdH0gcHJvcHMgQmFnIG9mIGN1cnJlbnQgcHJvcGVydHkgY2hhbmdlcwogICAgICAgKiBAcGFyYW0ge09iamVjdH0gb2xkUHJvcHMgQmFnIG9mIHByZXZpb3VzIHZhbHVlcyBmb3IgY2hhbmdlZCBwcm9wZXJ0aWVzCiAgICAgICAqIEBwYXJhbSB7Ym9vbGVhbn0gaGFzUGF0aHMgVHJ1ZSB3aXRoIGBwcm9wc2AgY29udGFpbnMgb25lIG9yIG1vcmUgcGF0aHMKICAgICAgICogQHJldHVybiB7Kn0gVmFsdWUgdGhlIGJpbmRpbmcgcGFydCBldmFsdWF0ZWQgdG8KICAgICAgICogQHByb3RlY3RlZAogICAgICAgKi8KICAgICAgc3RhdGljIF9ldmFsdWF0ZUJpbmRpbmcoaW5zdCwgcGFydCwgcGF0aCwgcHJvcHMsIG9sZFByb3BzLCBoYXNQYXRocykgewogICAgICAgIGxldCB2YWx1ZTsKICAgICAgICBpZiAocGFydC5zaWduYXR1cmUpIHsKICAgICAgICAgIHZhbHVlID0gcnVuTWV0aG9kRWZmZWN0KGluc3QsIHBhdGgsIHByb3BzLCBvbGRQcm9wcywgcGFydC5zaWduYXR1cmUpOwogICAgICAgIH0gZWxzZSBpZiAocGF0aCAhPSBwYXJ0LnNvdXJjZSkgewogICAgICAgICAgdmFsdWUgPSBQb2x5bWVyLlBhdGguZ2V0KGluc3QsIHBhcnQuc291cmNlKTsKICAgICAgICB9IGVsc2UgewogICAgICAgICAgaWYgKGhhc1BhdGhzICYmIFBvbHltZXIuUGF0aC5pc1BhdGgocGF0aCkpIHsKICAgICAgICAgICAgdmFsdWUgPSBQb2x5bWVyLlBhdGguZ2V0KGluc3QsIHBhdGgpOwogICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgdmFsdWUgPSBpbnN0Ll9fZGF0YVtwYXRoXTsKICAgICAgICAgIH0KICAgICAgICB9CiAgICAgICAgaWYgKHBhcnQubmVnYXRlKSB7CiAgICAgICAgICB2YWx1ZSA9ICF2YWx1ZTsKICAgICAgICB9CiAgICAgICAgcmV0dXJuIHZhbHVlOwogICAgICB9CgogICAgfQoKICAgIC8vIG1ha2UgYSB0eXBpbmcgZm9yIGNsb3N1cmUgOlAKICAgIFByb3BlcnR5RWZmZWN0c1R5cGUgPSBQcm9wZXJ0eUVmZmVjdHM7CgogICAgcmV0dXJuIFByb3BlcnR5RWZmZWN0czsKICB9KTsKCiAgLyoqCiAgICogSGVscGVyIGFwaSBmb3IgZW5xdWV1aW5nIGNsaWVudCBkb20gY3JlYXRlZCBieSBhIGhvc3QgZWxlbWVudC4KICAgKgogICAqIEJ5IGRlZmF1bHQgZWxlbWVudHMgYXJlIGZsdXNoZWQgdmlhIGBfZmx1c2hQcm9wZXJ0aWVzYCB3aGVuCiAgICogYGNvbm5lY3RlZENhbGxiYWNrYCBpcyBjYWxsZWQuIEVsZW1lbnRzIGF0dGFjaCB0aGVpciBjbGllbnQgZG9tIHRvCiAgICogdGhlbXNlbHZlcyBhdCBgcmVhZHlgIHRpbWUgd2hpY2ggcmVzdWx0cyBmcm9tIHRoaXMgZmlyc3QgZmx1c2guCiAgICogVGhpcyBwcm92aWRlcyBhbiBvcmRlcmluZyBndWFyYW50ZWUgdGhhdCB0aGUgY2xpZW50IGRvbSBhbiBlbGVtZW50CiAgICogY3JlYXRlcyBpcyBmbHVzaGVkIGJlZm9yZSB0aGUgZWxlbWVudCBpdHNlbGYgKGkuZS4gY2xpZW50IGByZWFkeWAKICAgKiBmaXJlcyBiZWZvcmUgaG9zdCBgcmVhZHlgKS4KICAgKgogICAqIEhvd2V2ZXIsIGlmIGBfZmx1c2hQcm9wZXJ0aWVzYCBpcyBjYWxsZWQgKmJlZm9yZSogYW4gZWxlbWVudCBpcyBjb25uZWN0ZWQsCiAgICogYXMgZm9yIGV4YW1wbGUgYFRlbXBsYXRpemVgIGRvZXMsIHRoaXMgb3JkZXJpbmcgZ3VhcmFudGVlIGNhbm5vdCBiZQogICAqIHNhdGlzZmllZCBiZWNhdXNlIG5vIGVsZW1lbnRzIGFyZSBjb25uZWN0ZWQuIChOb3RlOiBCb3VuZCBlbGVtZW50cyB0aGF0CiAgICogcmVjZWl2ZSBkYXRhIGRvIGJlY29tZSBlbnF1ZXVlZCBjbGllbnRzIGFuZCBhcmUgcHJvcGVybHkgb3JkZXJlZCBidXQKICAgKiB1bmJvdW5kIGVsZW1lbnRzIGFyZSBub3QuKQogICAqCiAgICogVG8gbWFpbnRhaW4gdGhlIGRlc2lyZWQgImNsaWVudCBiZWZvcmUgaG9zdCIgb3JkZXJpbmcgZ3VhcmFudGVlIGZvciB0aGlzCiAgICogY2FzZSB3ZSByZWx5IG9uIHRoZSAiaG9zdCBzdGFjay4gQ2xpZW50IG5vZGVzIHJlZ2lzdGVycyB0aGVtc2VsdmVzIHdpdGgKICAgKiB0aGUgY3JlYXRpbmcgaG9zdCBlbGVtZW50IHdoZW4gY3JlYXRlZC4gVGhpcyBlbnN1cmVzIHRoYXQgYWxsIGNsaWVudCBkb20KICAgKiBpcyByZWFkaWVkIGluIHRoZSBwcm9wZXIgb3JkZXIsIG1haW50YWluaW5nIHRoZSBkZXNpcmVkIGd1YXJhbnRlZS4KICAgKgogICAqIEBwcml2YXRlCiAgICovCiAgbGV0IGhvc3RTdGFjayA9IHsKCiAgICBzdGFjazogW10sCgogICAgLyoqCiAgICAgKiBAcGFyYW0geyp9IGluc3QgSW5zdGFuY2UgdG8gYWRkIHRvIGhvc3RTdGFjawogICAgICogQHJldHVybiB7dm9pZH0KICAgICAqIEB0aGlzIHtob3N0U3RhY2t9CiAgICAgKi8KICAgIHJlZ2lzdGVySG9zdChpbnN0KSB7CiAgICAgIGlmICh0aGlzLnN0YWNrLmxlbmd0aCkgewogICAgICAgIGxldCBob3N0ID0gdGhpcy5zdGFja1t0aGlzLnN0YWNrLmxlbmd0aC0xXTsKICAgICAgICBob3N0Ll9lbnF1ZXVlQ2xpZW50KGluc3QpOwogICAgICB9CiAgICB9LAoKICAgIC8qKgogICAgICogQHBhcmFtIHsqfSBpbnN0IEluc3RhbmNlIHRvIGJlZ2luIGhvc3RpbmcKICAgICAqIEByZXR1cm4ge3ZvaWR9CiAgICAgKiBAdGhpcyB7aG9zdFN0YWNrfQogICAgICovCiAgICBiZWdpbkhvc3RpbmcoaW5zdCkgewogICAgICB0aGlzLnN0YWNrLnB1c2goaW5zdCk7CiAgICB9LAoKICAgIC8qKgogICAgICogQHBhcmFtIHsqfSBpbnN0IEluc3RhbmNlIHRvIGVuZCBob3N0aW5nCiAgICAgKiBAcmV0dXJuIHt2b2lkfQogICAgICogQHRoaXMge2hvc3RTdGFja30KICAgICAqLwogICAgZW5kSG9zdGluZyhpbnN0KSB7CiAgICAgIGxldCBzdGFja0xlbiA9IHRoaXMuc3RhY2subGVuZ3RoOwogICAgICBpZiAoc3RhY2tMZW4gJiYgdGhpcy5zdGFja1tzdGFja0xlbi0xXSA9PSBpbnN0KSB7CiAgICAgICAgdGhpcy5zdGFjay5wb3AoKTsKICAgICAgfQogICAgfQoKICB9OwoKfSkoKTsKPC9zY3JpcHQ+PHNjcmlwdD4KKGZ1bmN0aW9uKCkgewogICd1c2Ugc3RyaWN0JzsKCiAgLyoqCiAgICogUHJvdmlkZXMgYmFzaWMgdHJhY2tpbmcgb2YgZWxlbWVudCBkZWZpbml0aW9ucyAocmVnaXN0cmF0aW9ucykgYW5kCiAgICogaW5zdGFuY2UgY291bnRzLgogICAqCiAgICogQG5hbWVzcGFjZQogICAqIEBzdW1tYXJ5IFByb3ZpZGVzIGJhc2ljIHRyYWNraW5nIG9mIGVsZW1lbnQgZGVmaW5pdGlvbnMgKHJlZ2lzdHJhdGlvbnMpIGFuZAogICAqIGluc3RhbmNlIGNvdW50cy4KICAgKi8KICBQb2x5bWVyLnRlbGVtZXRyeSA9IHsKICAgIC8qKgogICAgICogVG90YWwgbnVtYmVyIG9mIFBvbHltZXIgZWxlbWVudCBpbnN0YW5jZXMgY3JlYXRlZC4KICAgICAqIEB0eXBlIHtudW1iZXJ9CiAgICAgKi8KICAgIGluc3RhbmNlQ291bnQ6IDAsCiAgICAvKioKICAgICAqIEFycmF5IG9mIFBvbHltZXIgZWxlbWVudCBjbGFzc2VzIHRoYXQgaGF2ZSBiZWVuIGZpbmFsaXplZC4KICAgICAqIEB0eXBlIHtBcnJheTxQb2x5bWVyLkVsZW1lbnQ+fQogICAgICovCiAgICByZWdpc3RyYXRpb25zOiBbXSwKICAgIC8qKgogICAgICogQHBhcmFtIHshUG9seW1lckVsZW1lbnRDb25zdHJ1Y3Rvcn0gcHJvdG90eXBlIEVsZW1lbnQgcHJvdG90eXBlIHRvIGxvZwogICAgICogQHRoaXMge3RoaXN9CiAgICAgKiBAcHJpdmF0ZQogICAgICovCiAgICBfcmVnTG9nOiBmdW5jdGlvbihwcm90b3R5cGUpIHsKICAgICAgY29uc29sZS5sb2coJ1snICsgcHJvdG90eXBlLmlzICsgJ106IHJlZ2lzdGVyZWQnKTsKICAgIH0sCiAgICAvKioKICAgICAqIFJlZ2lzdGVycyBhIGNsYXNzIHByb3RvdHlwZSBmb3IgdGVsZW1ldHJ5IHB1cnBvc2VzLgogICAgICogQHBhcmFtIHtIVE1MRWxlbWVudH0gcHJvdG90eXBlIEVsZW1lbnQgcHJvdG90eXBlIHRvIHJlZ2lzdGVyCiAgICAgKiBAdGhpcyB7dGhpc30KICAgICAqIEBwcm90ZWN0ZWQKICAgICAqLwogICAgcmVnaXN0ZXI6IGZ1bmN0aW9uKHByb3RvdHlwZSkgewogICAgICB0aGlzLnJlZ2lzdHJhdGlvbnMucHVzaChwcm90b3R5cGUpOwogICAgICBQb2x5bWVyLmxvZyAmJiB0aGlzLl9yZWdMb2cocHJvdG90eXBlKTsKICAgIH0sCiAgICAvKioKICAgICAqIExvZ3MgYWxsIGVsZW1lbnRzIHJlZ2lzdGVyZWQgd2l0aCBhbiBgaXNgIHRvIHRoZSBjb25zb2xlLgogICAgICogQHB1YmxpYwogICAgICogQHRoaXMge3RoaXN9CiAgICAgKi8KICAgIGR1bXBSZWdpc3RyYXRpb25zOiBmdW5jdGlvbigpIHsKICAgICAgdGhpcy5yZWdpc3RyYXRpb25zLmZvckVhY2godGhpcy5fcmVnTG9nKTsKICAgIH0KICB9OwoKfSkoKTsKPC9zY3JpcHQ+PHNjcmlwdD4KKGZ1bmN0aW9uKCkgewogICd1c2Ugc3RyaWN0JzsKCiAgLyoqCiAgICogQ3JlYXRlcyBhIGNvcHkgb2YgYHByb3BzYCB3aXRoIGVhY2ggcHJvcGVydHkgbm9ybWFsaXplZCBzdWNoIHRoYXQKICAgKiB1cGdyYWRlZCBpdCBpcyBhbiBvYmplY3Qgd2l0aCBhdCBsZWFzdCBhIHR5cGUgcHJvcGVydHkgeyB0eXBlOiBUeXBlfS4KICAgKgogICAqIEBwYXJhbSB7T2JqZWN0fSBwcm9wcyBQcm9wZXJ0aWVzIHRvIG5vcm1hbGl6ZQogICAqIEByZXR1cm4ge09iamVjdH0gQ29weSBvZiBpbnB1dCBgcHJvcHNgIHdpdGggbm9ybWFsaXplZCBwcm9wZXJ0aWVzIHRoYXQKICAgKiBhcmUgaW4gdGhlIGZvcm0ge3R5cGU6IFR5cGV9CiAgICogQHByaXZhdGUKICAgKi8KICBmdW5jdGlvbiBub3JtYWxpemVQcm9wZXJ0aWVzKHByb3BzKSB7CiAgICBjb25zdCBvdXRwdXQgPSB7fTsKICAgIGZvciAobGV0IHAgaW4gcHJvcHMpIHsKICAgICAgY29uc3QgbyA9IHByb3BzW3BdOwogICAgICBvdXRwdXRbcF0gPSAodHlwZW9mIG8gPT09ICdmdW5jdGlvbicpID8ge3R5cGU6IG99IDogbzsKICAgIH0KICAgIHJldHVybiBvdXRwdXQ7CiAgfQoKICAvKioKICAgKiBNaXhpbiB0aGF0IHByb3ZpZGVzIGEgbWluaW1hbCBzdGFydGluZyBwb2ludCB0byB1c2luZyB0aGUgUHJvcGVydGllc0NoYW5nZWQKICAgKiBtaXhpbiBieSBwcm92aWRpbmcgYSBtZWNoYW5pc20gdG8gZGVjbGFyZSBwcm9wZXJ0aWVzIGluIGEgc3RhdGljCiAgICogZ2V0dGVyIChlLmcuIHN0YXRpYyBnZXQgcHJvcGVydGllcygpIHsgcmV0dXJuIHsgZm9vOiBTdHJpbmcgfSB9KS4gQ2hhbmdlcwogICAqIGFyZSByZXBvcnRlZCB2aWEgdGhlIGBfcHJvcGVydGllc0NoYW5nZWRgIG1ldGhvZC4KICAgKgogICAqIFRoaXMgbWl4aW4gcHJvdmlkZXMgbm8gc3BlY2lmaWMgc3VwcG9ydCBmb3IgcmVuZGVyaW5nLiBVc2VycyBhcmUgZXhwZWN0ZWQKICAgKiB0byBjcmVhdGUgYSBTaGFkb3dSb290IGFuZCBwdXQgY29udGVudCBpbnRvIGl0IGFuZCB1cGRhdGUgaXQgaW4gd2hhdGV2ZXIKICAgKiB3YXkgbWFrZXMgc2Vuc2UuIFRoaXMgY2FuIGJlIGRvbmUgaW4gcmVhY3Rpb24gdG8gcHJvcGVydGllcyBjaGFuZ2luZyBieQogICAqIGltcGxlbWVudGluZyBgX3Byb3BlcnRpZXNDaGFuZ2VkYC4KICAgKgogICAqIEBtaXhpbkZ1bmN0aW9uCiAgICogQHBvbHltZXIKICAgKiBAYXBwbGllc01peGluIFBvbHltZXIuUHJvcGVydGllc0NoYW5nZWQKICAgKiBAbWVtYmVyb2YgUG9seW1lcgogICAqIEBzdW1tYXJ5IE1peGluIHRoYXQgcHJvdmlkZXMgYSBtaW5pbWFsIHN0YXJ0aW5nIHBvaW50IGZvciB1c2luZwogICAqIHRoZSBQcm9wZXJ0aWVzQ2hhbmdlZCBtaXhpbiBieSBwcm92aWRpbmcgYSBkZWNsYXJhdGl2ZSBgcHJvcGVydGllc2Agb2JqZWN0LgogICAqLwogICBQb2x5bWVyLlByb3BlcnRpZXNNaXhpbiA9IFBvbHltZXIuZGVkdXBpbmdNaXhpbihzdXBlckNsYXNzID0+IHsKCiAgICAvKioKICAgICAqIEBjb25zdHJ1Y3RvcgogICAgICogQGV4dGVuZHMge3N1cGVyQ2xhc3N9CiAgICAgKiBAaW1wbGVtZW50cyB7UG9seW1lcl9Qcm9wZXJ0aWVzQ2hhbmdlZH0KICAgICAqIEBwcml2YXRlCiAgICAgKi8KICAgIGNvbnN0IGJhc2UgPSBQb2x5bWVyLlByb3BlcnRpZXNDaGFuZ2VkKHN1cGVyQ2xhc3MpOwoKICAgIC8qKgogICAgICogUmV0dXJucyB0aGUgc3VwZXIgY2xhc3MgY29uc3RydWN0b3IgZm9yIHRoZSBnaXZlbiBjbGFzcywgaWYgaXQgaXMgYW4KICAgICAqIGluc3RhbmNlIG9mIHRoZSBQcm9wZXJ0aWVzTWl4aW4uCiAgICAgKgogICAgICogQHBhcmFtIHshUHJvcGVydGllc01peGluQ29uc3RydWN0b3J9IGNvbnN0cnVjdG9yIFByb3BlcnRpZXNNaXhpbiBjb25zdHJ1Y3RvcgogICAgICogQHJldHVybiB7UHJvcGVydGllc01peGluQ29uc3RydWN0b3J9IFN1cGVyIGNsYXNzIGNvbnN0cnVjdG9yCiAgICAgKi8KICAgIGZ1bmN0aW9uIHN1cGVyUHJvcGVydGllc0NsYXNzKGNvbnN0cnVjdG9yKSB7CiAgICAgIGNvbnN0IHN1cGVyQ3RvciA9IE9iamVjdC5nZXRQcm90b3R5cGVPZihjb25zdHJ1Y3Rvcik7CgogICAgICAvLyBOb3RlLCB0aGUgYFByb3BlcnRpZXNNaXhpbmAgY2xhc3MgYmVsb3cgb25seSByZWZlcnMgdG8gdGhlIGNsYXNzCiAgICAgIC8vIGdlbmVyYXRlZCBieSB0aGlzIGNhbGwgdG8gdGhlIG1peGluOyB0aGUgaW5zdGFuY2VvZiB0ZXN0IG9ubHkgd29ya3MKICAgICAgLy8gYmVjYXVzZSB0aGUgbWl4aW4gaXMgZGVkdXBlZCBhbmQgZ3VhcmFudGVlZCBvbmx5IHRvIGFwcGx5IG9uY2UsIGhlbmNlCiAgICAgIC8vIGFsbCBjb25zdHJ1Y3RvcnMgaW4gYSBwcm90byBjaGFpbiB3aWxsIHNlZSB0aGUgc2FtZSBgUHJvcGVydGllc01peGluYAogICAgICByZXR1cm4gKHN1cGVyQ3Rvci5wcm90b3R5cGUgaW5zdGFuY2VvZiBQcm9wZXJ0aWVzTWl4aW4pID8KICAgICAgICAvKiogQHR5cGUge1Byb3BlcnRpZXNNaXhpbkNvbnN0cnVjdG9yfSAqLyAoc3VwZXJDdG9yKSA6IG51bGw7CiAgICB9CgogICAgLyoqCiAgICAgKiBSZXR1cm5zIGEgbWVtb2l6ZWQgdmVyc2lvbiBvZiB0aGUgYHByb3BlcnRpZXNgIG9iamVjdCBmb3IgdGhlCiAgICAgKiBnaXZlbiBjbGFzcy4gUHJvcGVydGllcyBub3QgaW4gb2JqZWN0IGZvcm1hdCBhcmUgY29udmVydGVkIHRvIGF0CiAgICAgKiBsZWFzdCB7dHlwZX0uCiAgICAgKgogICAgICogQHBhcmFtIHtQcm9wZXJ0aWVzTWl4aW5Db25zdHJ1Y3Rvcn0gY29uc3RydWN0b3IgUHJvcGVydGllc01peGluIGNvbnN0cnVjdG9yCiAgICAgKiBAcmV0dXJuIHtPYmplY3R9IE1lbW9pemVkIHByb3BlcnRpZXMgb2JqZWN0CiAgICAgKi8KICAgIGZ1bmN0aW9uIG93blByb3BlcnRpZXMoY29uc3RydWN0b3IpIHsKICAgICAgaWYgKCFjb25zdHJ1Y3Rvci5oYXNPd25Qcm9wZXJ0eShKU0NvbXBpbGVyX3JlbmFtZVByb3BlcnR5KCdfX293blByb3BlcnRpZXMnLCBjb25zdHJ1Y3RvcikpKSB7CiAgICAgICAgbGV0IHByb3BzID0gbnVsbDsKCiAgICAgICAgaWYgKGNvbnN0cnVjdG9yLmhhc093blByb3BlcnR5KEpTQ29tcGlsZXJfcmVuYW1lUHJvcGVydHkoJ3Byb3BlcnRpZXMnLCBjb25zdHJ1Y3RvcikpKSB7CiAgICAgICAgICBjb25zdCBwcm9wZXJ0aWVzID0gY29uc3RydWN0b3IucHJvcGVydGllczsKICAgICAgICAgIAogICAgICAgICAgaWYgKHByb3BlcnRpZXMpIHsKICAgICAgICAgICAgcHJvcHMgPSBub3JtYWxpemVQcm9wZXJ0aWVzKHByb3BlcnRpZXMpOwogICAgICAgICAgfQogICAgICAgIH0KCiAgICAgICAgY29uc3RydWN0b3IuX19vd25Qcm9wZXJ0aWVzID0gcHJvcHM7CiAgICAgIH0KICAgICAgcmV0dXJuIGNvbnN0cnVjdG9yLl9fb3duUHJvcGVydGllczsKICAgIH0KCiAgICAvKioKICAgICAqIEBwb2x5bWVyCiAgICAgKiBAbWl4aW5DbGFzcwogICAgICogQGV4dGVuZHMge2Jhc2V9CiAgICAgKiBAaW1wbGVtZW50cyB7UG9seW1lcl9Qcm9wZXJ0aWVzTWl4aW59CiAgICAgKiBAdW5yZXN0cmljdGVkCiAgICAgKi8KICAgIGNsYXNzIFByb3BlcnRpZXNNaXhpbiBleHRlbmRzIGJhc2UgewoKICAgICAgLyoqCiAgICAgICAqIEltcGxlbWVudHMgc3RhbmRhcmQgY3VzdG9tIGVsZW1lbnRzIGdldHRlciB0byBvYnNlcnZlcyB0aGUgYXR0cmlidXRlcwogICAgICAgKiBsaXN0ZWQgaW4gYHByb3BlcnRpZXNgLgogICAgICAgKiBAc3VwcHJlc3Mge21pc3NpbmdQcm9wZXJ0aWVzfSBJbnRlcmZhY2VzIGluIGNsb3N1cmUgZG8gbm90IGluaGVyaXQgc3RhdGljcywgYnV0IGNsYXNzZXMgZG8KICAgICAgICovCiAgICAgIHN0YXRpYyBnZXQgb2JzZXJ2ZWRBdHRyaWJ1dGVzKCkgewogICAgICAgIGlmICghdGhpcy5oYXNPd25Qcm9wZXJ0eSgnX19vYnNlcnZlZEF0dHJpYnV0ZXMnKSkgewogICAgICAgICAgUG9seW1lci50ZWxlbWV0cnkucmVnaXN0ZXIodGhpcy5wcm90b3R5cGUpOwogICAgICAgICAgY29uc3QgcHJvcHMgPSB0aGlzLl9wcm9wZXJ0aWVzOwogICAgICAgICAgdGhpcy5fX29ic2VydmVkQXR0cmlidXRlcyA9IHByb3BzID8gT2JqZWN0LmtleXMocHJvcHMpLm1hcChwID0+IHRoaXMuYXR0cmlidXRlTmFtZUZvclByb3BlcnR5KHApKSA6IFtdOwogICAgICAgIH0KICAgICAgICByZXR1cm4gdGhpcy5fX29ic2VydmVkQXR0cmlidXRlczsKICAgICAgfQoKICAgICAgLyoqCiAgICAgICAqIEZpbmFsaXplcyBhbiBlbGVtZW50IGRlZmluaXRpb24sIGluY2x1ZGluZyBlbnN1cmluZyBhbnkgc3VwZXIgY2xhc3NlcwogICAgICAgKiBhcmUgYWxzbyBmaW5hbGl6ZWQuIFRoaXMgaW5jbHVkZXMgZW5zdXJpbmcgcHJvcGVydHkKICAgICAgICogYWNjZXNzb3JzIGV4aXN0IG9uIHRoZSBlbGVtZW50IHByb3RvdHlwZS4gVGhpcyBtZXRob2QgY2FsbHMKICAgICAgICogYF9maW5hbGl6ZUNsYXNzYCB0byBmaW5hbGl6ZSBlYWNoIGNvbnN0cnVjdG9yIGluIHRoZSBwcm90b3R5cGUgY2hhaW4uCiAgICAgICAqIEByZXR1cm4ge3ZvaWR9CiAgICAgICAqLwogICAgICBzdGF0aWMgZmluYWxpemUoKSB7CiAgICAgICAgaWYgKCF0aGlzLmhhc093blByb3BlcnR5KEpTQ29tcGlsZXJfcmVuYW1lUHJvcGVydHkoJ19fZmluYWxpemVkJywgdGhpcykpKSB7CiAgICAgICAgICBjb25zdCBzdXBlckN0b3IgPSBzdXBlclByb3BlcnRpZXNDbGFzcygvKiogQHR5cGUge1Byb3BlcnRpZXNNaXhpbkNvbnN0cnVjdG9yfSAqLyh0aGlzKSk7CiAgICAgICAgICBpZiAoc3VwZXJDdG9yKSB7CiAgICAgICAgICAgIHN1cGVyQ3Rvci5maW5hbGl6ZSgpOwogICAgICAgICAgfQogICAgICAgICAgdGhpcy5fX2ZpbmFsaXplZCA9IHRydWU7CiAgICAgICAgICB0aGlzLl9maW5hbGl6ZUNsYXNzKCk7CiAgICAgICAgfQogICAgICB9CgogICAgICAvKioKICAgICAgICogRmluYWxpemUgYW4gZWxlbWVudCBjbGFzcy4gVGhpcyBpbmNsdWRlcyBlbnN1cmluZyBwcm9wZXJ0eQogICAgICAgKiBhY2Nlc3NvcnMgZXhpc3Qgb24gdGhlIGVsZW1lbnQgcHJvdG90eXBlLiBUaGlzIG1ldGhvZCBpcyBjYWxsZWQgYnkKICAgICAgICogYGZpbmFsaXplYCBhbmQgZmluYWxpemVzIHRoZSBjbGFzcyBjb25zdHJ1Y3Rvci4KICAgICAgICoKICAgICAgICogQHByb3RlY3RlZAogICAgICAgKi8KICAgICAgc3RhdGljIF9maW5hbGl6ZUNsYXNzKCkgewogICAgICAgIGNvbnN0IHByb3BzID0gb3duUHJvcGVydGllcygvKiogQHR5cGUge1Byb3BlcnRpZXNNaXhpbkNvbnN0cnVjdG9yfSAqLyh0aGlzKSk7CiAgICAgICAgaWYgKHByb3BzKSB7CiAgICAgICAgICB0aGlzLmNyZWF0ZVByb3BlcnRpZXMocHJvcHMpOwogICAgICAgIH0KICAgICAgfQoKICAgICAgLyoqCiAgICAgICAqIFJldHVybnMgYSBtZW1vaXplZCB2ZXJzaW9uIG9mIGFsbCBwcm9wZXJ0aWVzLCBpbmNsdWRpbmcgdGhvc2UgaW5oZXJpdGVkCiAgICAgICAqIGZyb20gc3VwZXIgY2xhc3Nlcy4gUHJvcGVydGllcyBub3QgaW4gb2JqZWN0IGZvcm1hdCBhcmUgY29udmVydGVkIHRvCiAgICAgICAqIGF0IGxlYXN0IHt0eXBlfS4KICAgICAgICoKICAgICAgICogQHJldHVybiB7T2JqZWN0fSBPYmplY3QgY29udGFpbmluZyBwcm9wZXJ0aWVzIGZvciB0aGlzIGNsYXNzCiAgICAgICAqIEBwcm90ZWN0ZWQKICAgICAgICovCiAgICAgIHN0YXRpYyBnZXQgX3Byb3BlcnRpZXMoKSB7CiAgICAgICAgaWYgKCF0aGlzLmhhc093blByb3BlcnR5KAogICAgICAgICAgSlNDb21waWxlcl9yZW5hbWVQcm9wZXJ0eSgnX19wcm9wZXJ0aWVzJywgdGhpcykpKSB7CiAgICAgICAgICBjb25zdCBzdXBlckN0b3IgPSBzdXBlclByb3BlcnRpZXNDbGFzcygvKiogQHR5cGUge1Byb3BlcnRpZXNNaXhpbkNvbnN0cnVjdG9yfSAqLyh0aGlzKSk7CiAgICAgICAgICB0aGlzLl9fcHJvcGVydGllcyA9IE9iamVjdC5hc3NpZ24oe30sCiAgICAgICAgICAgIHN1cGVyQ3RvciAmJiBzdXBlckN0b3IuX3Byb3BlcnRpZXMsCiAgICAgICAgICAgIG93blByb3BlcnRpZXMoLyoqIEB0eXBlIHtQcm9wZXJ0aWVzTWl4aW5Db25zdHJ1Y3Rvcn0gKi8odGhpcykpKTsKICAgICAgICB9CiAgICAgICAgcmV0dXJuIHRoaXMuX19wcm9wZXJ0aWVzOwogICAgICB9CgogICAgICAvKioKICAgICAgICogT3ZlcnJpZGVzIGBQcm9wZXJ0aWVzQ2hhbmdlZGAgbWV0aG9kIHRvIHJldHVybiB0eXBlIHNwZWNpZmllZCBpbiB0aGUKICAgICAgICogc3RhdGljIGBwcm9wZXJ0aWVzYCBvYmplY3QgZm9yIHRoZSBnaXZlbiBwcm9wZXJ0eS4KICAgICAgICogQHBhcmFtIHtzdHJpbmd9IG5hbWUgTmFtZSBvZiBwcm9wZXJ0eQogICAgICAgKiBAcmV0dXJuIHsqfSBUeXBlIHRvIHdoaWNoIHRvIGRlc2VyaWFsaXplIGF0dHJpYnV0ZQogICAgICAgKgogICAgICAgKiBAcHJvdGVjdGVkCiAgICAgICAqLwogICAgICBzdGF0aWMgdHlwZUZvclByb3BlcnR5KG5hbWUpIHsKICAgICAgICBjb25zdCBpbmZvID0gdGhpcy5fcHJvcGVydGllc1tuYW1lXTsKICAgICAgICByZXR1cm4gaW5mbyAmJiBpbmZvLnR5cGU7CiAgICAgIH0KCiAgICAgIC8qKgogICAgICAgKiBPdmVycmlkZXMgYFByb3BlcnRpZXNDaGFuZ2VkYCBtZXRob2QgYW5kIGFkZHMgYSBjYWxsIHRvCiAgICAgICAqIGBmaW5hbGl6ZWAgd2hpY2ggbGF6aWx5IGNvbmZpZ3VyZXMgdGhlIGVsZW1lbnQncyBwcm9wZXJ0eSBhY2Nlc3NvcnMuCiAgICAgICAqIEBvdmVycmlkZQogICAgICAgKiBAcmV0dXJuIHt2b2lkfQogICAgICAgKi8KICAgICAgX2luaXRpYWxpemVQcm9wZXJ0aWVzKCkgewogICAgICAgIFBvbHltZXIudGVsZW1ldHJ5Lmluc3RhbmNlQ291bnQrKzsKICAgICAgICB0aGlzLmNvbnN0cnVjdG9yLmZpbmFsaXplKCk7CiAgICAgICAgc3VwZXIuX2luaXRpYWxpemVQcm9wZXJ0aWVzKCk7CiAgICAgIH0KCiAgICAgIC8qKgogICAgICAgKiBDYWxsZWQgd2hlbiB0aGUgZWxlbWVudCBpcyBhZGRlZCB0byBhIGRvY3VtZW50LgogICAgICAgKiBDYWxscyBgX2VuYWJsZVByb3BlcnRpZXNgIHRvIHR1cm4gb24gcHJvcGVydHkgc3lzdGVtIGZyb20KICAgICAgICogYFByb3BlcnRpZXNDaGFuZ2VkYC4KICAgICAgICogQHN1cHByZXNzIHttaXNzaW5nUHJvcGVydGllc30gU3VwZXIgbWF5IG9yIG1heSBub3QgaW1wbGVtZW50IHRoZSBjYWxsYmFjawogICAgICAgKiBAcmV0dXJuIHt2b2lkfQogICAgICAgKi8KICAgICAgY29ubmVjdGVkQ2FsbGJhY2soKSB7CiAgICAgICAgaWYgKHN1cGVyLmNvbm5lY3RlZENhbGxiYWNrKSB7CiAgICAgICAgICBzdXBlci5jb25uZWN0ZWRDYWxsYmFjaygpOwogICAgICAgIH0KICAgICAgICB0aGlzLl9lbmFibGVQcm9wZXJ0aWVzKCk7CiAgICAgIH0KCiAgICAgIC8qKgogICAgICAgKiBDYWxsZWQgd2hlbiB0aGUgZWxlbWVudCBpcyByZW1vdmVkIGZyb20gYSBkb2N1bWVudAogICAgICAgKiBAc3VwcHJlc3Mge21pc3NpbmdQcm9wZXJ0aWVzfSBTdXBlciBtYXkgb3IgbWF5IG5vdCBpbXBsZW1lbnQgdGhlIGNhbGxiYWNrCiAgICAgICAqIEByZXR1cm4ge3ZvaWR9CiAgICAgICAqLwogICAgICBkaXNjb25uZWN0ZWRDYWxsYmFjaygpIHsKICAgICAgICBpZiAoc3VwZXIuZGlzY29ubmVjdGVkQ2FsbGJhY2spIHsKICAgICAgICAgIHN1cGVyLmRpc2Nvbm5lY3RlZENhbGxiYWNrKCk7CiAgICAgICAgfQogICAgICB9CgogICAgfQoKICAgIHJldHVybiBQcm9wZXJ0aWVzTWl4aW47CgogIH0pOwoKfSkoKTsKCjwvc2NyaXB0PjxzY3JpcHQ+CihmdW5jdGlvbigpIHsKICAndXNlIHN0cmljdCc7CgogIGNvbnN0IGJ1aWx0Q1NTID0gd2luZG93LlNoYWR5Q1NTICYmIHdpbmRvdy5TaGFkeUNTU1snY3NzQnVpbGQnXTsKCiAgLyoqCiAgICogRWxlbWVudCBjbGFzcyBtaXhpbiB0aGF0IHByb3ZpZGVzIHRoZSBjb3JlIEFQSSBmb3IgUG9seW1lcidzIG1ldGEtcHJvZ3JhbW1pbmcKICAgKiBmZWF0dXJlcyBpbmNsdWRpbmcgdGVtcGxhdGUgc3RhbXBpbmcsIGRhdGEtYmluZGluZywgYXR0cmlidXRlIGRlc2VyaWFsaXphdGlvbiwKICAgKiBhbmQgcHJvcGVydHkgY2hhbmdlIG9ic2VydmF0aW9uLgogICAqCiAgICogU3ViY2xhc3NlcnMgbWF5IHByb3ZpZGUgdGhlIGZvbGxvd2luZyBzdGF0aWMgZ2V0dGVycyB0byByZXR1cm4gbWV0YWRhdGEKICAgKiB1c2VkIHRvIGNvbmZpZ3VyZSBQb2x5bWVyJ3MgZmVhdHVyZXMgZm9yIHRoZSBjbGFzczoKICAgKgogICAqIC0gYHN0YXRpYyBnZXQgaXMoKWA6IFdoZW4gdGhlIHRlbXBsYXRlIGlzIHByb3ZpZGVkIHZpYSBhIGBkb20tbW9kdWxlYCwKICAgKiAgIHVzZXJzIHNob3VsZCByZXR1cm4gdGhlIGBkb20tbW9kdWxlYCBpZCBmcm9tIGEgc3RhdGljIGBpc2AgZ2V0dGVyLiAgSWYKICAgKiAgIG5vIHRlbXBsYXRlIGlzIG5lZWRlZCBvciB0aGUgdGVtcGxhdGUgaXMgcHJvdmlkZWQgZGlyZWN0bHkgdmlhIHRoZQogICAqICAgYHRlbXBsYXRlYCBnZXR0ZXIsIHRoZXJlIGlzIG5vIG5lZWQgdG8gZGVmaW5lIGBpc2AgZm9yIHRoZSBlbGVtZW50LgogICAqCiAgICogLSBgc3RhdGljIGdldCB0ZW1wbGF0ZSgpYDogVXNlcnMgbWF5IHByb3ZpZGUgdGhlIHRlbXBsYXRlIGRpcmVjdGx5IChhcwogICAqICAgb3Bwb3NlZCB0byB2aWEgYGRvbS1tb2R1bGVgKSBieSBpbXBsZW1lbnRpbmcgYSBzdGF0aWMgYHRlbXBsYXRlYCBnZXR0ZXIuCiAgICogICBUaGUgZ2V0dGVyIG1heSByZXR1cm4gYW4gYEhUTUxUZW1wbGF0ZUVsZW1lbnRgIG9yIGEgc3RyaW5nLCB3aGljaCB3aWxsCiAgICogICBhdXRvbWF0aWNhbGx5IGJlIHBhcnNlZCBpbnRvIGEgdGVtcGxhdGUuCiAgICoKICAgKiAtIGBzdGF0aWMgZ2V0IHByb3BlcnRpZXMoKWA6IFNob3VsZCByZXR1cm4gYW4gb2JqZWN0IGRlc2NyaWJpbmcKICAgKiAgIHByb3BlcnR5LXJlbGF0ZWQgbWV0YWRhdGEgdXNlZCBieSBQb2x5bWVyIGZlYXR1cmVzIChrZXk6IHByb3BlcnR5IG5hbWUKICAgKiAgIHZhbHVlOiBvYmplY3QgY29udGFpbmluZyBwcm9wZXJ0eSBtZXRhZGF0YSkuIFZhbGlkIGtleXMgaW4gcGVyLXByb3BlcnR5CiAgICogICBtZXRhZGF0YSBpbmNsdWRlOgogICAqICAgLSBgdHlwZWAgKFN0cmluZ3xOdW1iZXJ8T2JqZWN0fEFycmF5fC4uLik6IFVzZWQgYnkKICAgKiAgICAgYGF0dHJpYnV0ZUNoYW5nZWRDYWxsYmFja2AgdG8gZGV0ZXJtaW5lIGhvdyBzdHJpbmctYmFzZWQgYXR0cmlidXRlcwogICAqICAgICBhcmUgZGVzZXJpYWxpemVkIHRvIEphdmFTY3JpcHQgcHJvcGVydHkgdmFsdWVzLgogICAqICAgLSBgbm90aWZ5YCAoYm9vbGVhbik6IENhdXNlcyBhIGNoYW5nZSBpbiB0aGUgcHJvcGVydHkgdG8gZmlyZSBhCiAgICogICAgIG5vbi1idWJibGluZyBldmVudCBjYWxsZWQgYDxwcm9wZXJ0eT4tY2hhbmdlZGAuIEVsZW1lbnRzIHRoYXQgaGF2ZQogICAqICAgICBlbmFibGVkIHR3by13YXkgYmluZGluZyB0byB0aGUgcHJvcGVydHkgdXNlIHRoaXMgZXZlbnQgdG8gb2JzZXJ2ZSBjaGFuZ2VzLgogICAqICAgLSBgcmVhZE9ubHlgIChib29sZWFuKTogQ3JlYXRlcyBhIGdldHRlciBmb3IgdGhlIHByb3BlcnR5LCBidXQgbm8gc2V0dGVyLgogICAqICAgICBUbyBzZXQgYSByZWFkLW9ubHkgcHJvcGVydHksIHVzZSB0aGUgcHJpdmF0ZSBzZXR0ZXIgbWV0aG9kCiAgICogICAgIGBfc2V0UHJvcGVydHkocHJvcGVydHksIHZhbHVlKWAuCiAgICogICAtIGBvYnNlcnZlcmAgKHN0cmluZyk6IE9ic2VydmVyIG1ldGhvZCBuYW1lIHRoYXQgd2lsbCBiZSBjYWxsZWQgd2hlbgogICAqICAgICB0aGUgcHJvcGVydHkgY2hhbmdlcy4gVGhlIGFyZ3VtZW50cyBvZiB0aGUgbWV0aG9kIGFyZQogICAqICAgICBgKHZhbHVlLCBwcmV2aW91c1ZhbHVlKWAuCiAgICogICAtIGBjb21wdXRlZGAgKHN0cmluZyk6IFN0cmluZyBkZXNjcmliaW5nIG1ldGhvZCBhbmQgZGVwZW5kZW50IHByb3BlcnRpZXMKICAgKiAgICAgZm9yIGNvbXB1dGluZyB0aGUgdmFsdWUgb2YgdGhpcyBwcm9wZXJ0eSAoZS5nLiBgJ2NvbXB1dGVGb28oYmFyLCB6b3QpJ2ApLgogICAqICAgICBDb21wdXRlZCBwcm9wZXJ0aWVzIGFyZSByZWFkLW9ubHkgYnkgZGVmYXVsdCBhbmQgY2FuIG9ubHkgYmUgY2hhbmdlZAogICAqICAgICB2aWEgdGhlIHJldHVybiB2YWx1ZSBvZiB0aGUgY29tcHV0aW5nIG1ldGhvZC4KICAgKgogICAqIC0gYHN0YXRpYyBnZXQgb2JzZXJ2ZXJzKClgOiBBcnJheSBvZiBzdHJpbmdzIGRlc2NyaWJpbmcgbXVsdGktcHJvcGVydHkKICAgKiAgIG9ic2VydmVyIG1ldGhvZHMgYW5kIHRoZWlyIGRlcGVuZGVudCBwcm9wZXJ0aWVzIChlLmcuCiAgICogICBgJ29ic2VydmVBQkMoYSwgYiwgYyknYCkuCiAgICoKICAgKiBUaGUgYmFzZSBjbGFzcyBwcm92aWRlcyBkZWZhdWx0IGltcGxlbWVudGF0aW9ucyBmb3IgdGhlIGZvbGxvd2luZyBzdGFuZGFyZAogICAqIGN1c3RvbSBlbGVtZW50IGxpZmVjeWNsZSBjYWxsYmFja3M7IHVzZXJzIG1heSBvdmVycmlkZSB0aGVzZSwgYnV0IHNob3VsZAogICAqIGNhbGwgdGhlIHN1cGVyIG1ldGhvZCB0byBlbnN1cmUKICAgKiAtIGBjb25zdHJ1Y3RvcmA6IFJ1biB3aGVuIHRoZSBlbGVtZW50IGlzIGNyZWF0ZWQgb3IgdXBncmFkZWQKICAgKiAtIGBjb25uZWN0ZWRDYWxsYmFja2A6IFJ1biBlYWNoIHRpbWUgdGhlIGVsZW1lbnQgaXMgY29ubmVjdGVkIHRvIHRoZQogICAqICAgZG9jdW1lbnQKICAgKiAtIGBkaXNjb25uZWN0ZWRDYWxsYmFja2A6IFJ1biBlYWNoIHRpbWUgdGhlIGVsZW1lbnQgaXMgZGlzY29ubmVjdGVkIGZyb20KICAgKiAgIHRoZSBkb2N1bWVudAogICAqIC0gYGF0dHJpYnV0ZUNoYW5nZWRDYWxsYmFja2A6IFJ1biBlYWNoIHRpbWUgYW4gYXR0cmlidXRlIGluCiAgICogICBgb2JzZXJ2ZWRBdHRyaWJ1dGVzYCBpcyBzZXQgb3IgcmVtb3ZlZCAobm90ZTogdGhpcyBlbGVtZW50J3MgZGVmYXVsdAogICAqICAgYG9ic2VydmVkQXR0cmlidXRlc2AgaW1wbGVtZW50YXRpb24gd2lsbCBhdXRvbWF0aWNhbGx5IHJldHVybiBhbiBhcnJheQogICAqICAgb2YgZGFzaC1jYXNlZCBhdHRyaWJ1dGVzIGJhc2VkIG9uIGBwcm9wZXJ0aWVzYCkKICAgKgogICAqIEBtaXhpbkZ1bmN0aW9uCiAgICogQHBvbHltZXIKICAgKiBAYXBwbGllc01peGluIFBvbHltZXIuUHJvcGVydHlFZmZlY3RzCiAgICogQGFwcGxpZXNNaXhpbiBQb2x5bWVyLlByb3BlcnRpZXNNaXhpbgogICAqIEBtZW1iZXJvZiBQb2x5bWVyCiAgICogQHByb3BlcnR5IHJvb3RQYXRoIHtzdHJpbmd9IFNldCB0byB0aGUgdmFsdWUgb2YgYFBvbHltZXIucm9vdFBhdGhgLAogICAqICAgd2hpY2ggZGVmYXVsdHMgdG8gdGhlIG1haW4gZG9jdW1lbnQgcGF0aAogICAqIEBwcm9wZXJ0eSBpbXBvcnRQYXRoIHtzdHJpbmd9IFNldCB0byB0aGUgdmFsdWUgb2YgdGhlIGNsYXNzJ3Mgc3RhdGljCiAgICogICBgaW1wb3J0UGF0aGAgcHJvcGVydHksIHdoaWNoIGRlZmF1bHRzIHRvIHRoZSBwYXRoIG9mIHRoaXMgZWxlbWVudCdzCiAgICogICBgZG9tLW1vZHVsZWAgKHdoZW4gYGlzYCBpcyB1c2VkKSwgYnV0IGNhbiBiZSBvdmVycmlkZGVuIGZvciBvdGhlcgogICAqICAgaW1wb3J0IHN0cmF0ZWdpZXMuCiAgICogQHN1bW1hcnkgRWxlbWVudCBjbGFzcyBtaXhpbiB0aGF0IHByb3ZpZGVzIHRoZSBjb3JlIEFQSSBmb3IgUG9seW1lcidzCiAgICogbWV0YS1wcm9ncmFtbWluZyBmZWF0dXJlcy4KICAgKi8KICBQb2x5bWVyLkVsZW1lbnRNaXhpbiA9IFBvbHltZXIuZGVkdXBpbmdNaXhpbihiYXNlID0+IHsKCiAgICAvKioKICAgICAqIEBjb25zdHJ1Y3RvcgogICAgICogQGV4dGVuZHMge2Jhc2V9CiAgICAgKiBAaW1wbGVtZW50cyB7UG9seW1lcl9Qcm9wZXJ0eUVmZmVjdHN9CiAgICAgKiBAaW1wbGVtZW50cyB7UG9seW1lcl9Qcm9wZXJ0aWVzTWl4aW59CiAgICAgKiBAcHJpdmF0ZQogICAgICovCiAgICBjb25zdCBwb2x5bWVyRWxlbWVudEJhc2UgPSBQb2x5bWVyLlByb3BlcnRpZXNNaXhpbihQb2x5bWVyLlByb3BlcnR5RWZmZWN0cyhiYXNlKSk7CgogICAgLyoqCiAgICAgKiBSZXR1cm5zIGEgbGlzdCBvZiBwcm9wZXJ0aWVzIHdpdGggZGVmYXVsdCB2YWx1ZXMuCiAgICAgKiBUaGlzIGxpc3QgaXMgY3JlYXRlZCBhcyBhbiBvcHRpbWl6YXRpb24gc2luY2UgaXQgaXMgYSBzdWJzZXQgb2YKICAgICAqIHRoZSBsaXN0IHJldHVybmVkIGZyb20gYF9wcm9wZXJ0aWVzYC4KICAgICAqIFRoaXMgbGlzdCBpcyB1c2VkIGluIGBfaW5pdGlhbGl6ZVByb3BlcnRpZXNgIHRvIHNldCBwcm9wZXJ0eSBkZWZhdWx0cy4KICAgICAqCiAgICAgKiBAcGFyYW0ge1BvbHltZXJFbGVtZW50Q29uc3RydWN0b3J9IGNvbnN0cnVjdG9yIEVsZW1lbnQgY2xhc3MKICAgICAqIEByZXR1cm4ge1BvbHltZXJFbGVtZW50UHJvcGVydGllc30gRmxhdHRlbmVkIHByb3BlcnRpZXMgZm9yIHRoaXMgY2xhc3MKICAgICAqICAgdGhhdCBoYXZlIGRlZmF1bHQgdmFsdWVzCiAgICAgKiBAcHJpdmF0ZQogICAgICovCiAgICBmdW5jdGlvbiBwcm9wZXJ0eURlZmF1bHRzKGNvbnN0cnVjdG9yKSB7CiAgICAgIGlmICghY29uc3RydWN0b3IuaGFzT3duUHJvcGVydHkoCiAgICAgICAgSlNDb21waWxlcl9yZW5hbWVQcm9wZXJ0eSgnX19wcm9wZXJ0eURlZmF1bHRzJywgY29uc3RydWN0b3IpKSkgewogICAgICAgIGNvbnN0cnVjdG9yLl9fcHJvcGVydHlEZWZhdWx0cyA9IG51bGw7CiAgICAgICAgbGV0IHByb3BzID0gY29uc3RydWN0b3IuX3Byb3BlcnRpZXM7CiAgICAgICAgZm9yIChsZXQgcCBpbiBwcm9wcykgewogICAgICAgICAgbGV0IGluZm8gPSBwcm9wc1twXTsKICAgICAgICAgIGlmICgndmFsdWUnIGluIGluZm8pIHsKICAgICAgICAgICAgY29uc3RydWN0b3IuX19wcm9wZXJ0eURlZmF1bHRzID0gY29uc3RydWN0b3IuX19wcm9wZXJ0eURlZmF1bHRzIHx8IHt9OwogICAgICAgICAgICBjb25zdHJ1Y3Rvci5fX3Byb3BlcnR5RGVmYXVsdHNbcF0gPSBpbmZvOwogICAgICAgICAgfQogICAgICAgIH0KICAgICAgfQogICAgICByZXR1cm4gY29uc3RydWN0b3IuX19wcm9wZXJ0eURlZmF1bHRzOwogICAgfQoKICAgIC8qKgogICAgICogUmV0dXJucyBhIG1lbW9pemVkIHZlcnNpb24gb2YgdGhlIGBvYnNlcnZlcnNgIGFycmF5LgogICAgICogQHBhcmFtIHtQb2x5bWVyRWxlbWVudENvbnN0cnVjdG9yfSBjb25zdHJ1Y3RvciBFbGVtZW50IGNsYXNzCiAgICAgKiBAcmV0dXJuIHtBcnJheX0gQXJyYXkgY29udGFpbmluZyBvd24gb2JzZXJ2ZXJzIGZvciB0aGUgZ2l2ZW4gY2xhc3MKICAgICAqIEBwcm90ZWN0ZWQKICAgICAqLwogICAgZnVuY3Rpb24gb3duT2JzZXJ2ZXJzKGNvbnN0cnVjdG9yKSB7CiAgICAgIGlmICghY29uc3RydWN0b3IuaGFzT3duUHJvcGVydHkoCiAgICAgICAgSlNDb21waWxlcl9yZW5hbWVQcm9wZXJ0eSgnX19vd25PYnNlcnZlcnMnLCBjb25zdHJ1Y3RvcikpKSB7CiAgICAgICAgICBjb25zdHJ1Y3Rvci5fX293bk9ic2VydmVycyA9CiAgICAgICAgICBjb25zdHJ1Y3Rvci5oYXNPd25Qcm9wZXJ0eShKU0NvbXBpbGVyX3JlbmFtZVByb3BlcnR5KCdvYnNlcnZlcnMnLCBjb25zdHJ1Y3RvcikpID8KICAgICAgICAgIC8qKiBAdHlwZSB7UG9seW1lckVsZW1lbnRDb25zdHJ1Y3Rvcn0gKi8gKGNvbnN0cnVjdG9yKS5vYnNlcnZlcnMgOiBudWxsOwogICAgICB9CiAgICAgIHJldHVybiBjb25zdHJ1Y3Rvci5fX293bk9ic2VydmVyczsKICAgIH0KCiAgICAvKioKICAgICAqIENyZWF0ZXMgZWZmZWN0cyBmb3IgYSBwcm9wZXJ0eS4KICAgICAqCiAgICAgKiBOb3RlLCBvbmNlIGEgcHJvcGVydHkgaGFzIGJlZW4gc2V0IHRvCiAgICAgKiBgcmVhZE9ubHlgLCBgY29tcHV0ZWRgLCBgcmVmbGVjdFRvQXR0cmlidXRlYCwgb3IgYG5vdGlmeWAKICAgICAqIHRoZXNlIHZhbHVlcyBtYXkgbm90IGJlIGNoYW5nZWQuIEZvciBleGFtcGxlLCBhIHN1YmNsYXNzIGNhbm5vdAogICAgICogYWx0ZXIgdGhlc2Ugc2V0dGluZ3MuIEhvd2V2ZXIsIGFkZGl0aW9uYWwgYG9ic2VydmVyc2AgbWF5IGJlIGFkZGVkCiAgICAgKiBieSBzdWJjbGFzc2VzLgogICAgICoKICAgICAqIFRoZSBpbmZvIG9iamVjdCBzaG91bGQgY29udGFpbiBwcm9wZXJ0eSBtZXRhZGF0YSBhcyBmb2xsb3dzOgogICAgICoKICAgICAqICogYHR5cGVgOiB7ZnVuY3Rpb259IHR5cGUgdG8gd2hpY2ggYW4gYXR0cmlidXRlIG1hdGNoaW5nIHRoZSBwcm9wZXJ0eQogICAgICogaXMgZGVzZXJpYWxpemVkLiBOb3RlIHRoZSBwcm9wZXJ0eSBpcyBjYW1lbC1jYXNlZCBmcm9tIGEgZGFzaC1jYXNlZAogICAgICogYXR0cmlidXRlLiBGb3IgZXhhbXBsZSwgJ2Zvby1iYXInIGF0dHJpYnV0ZSBpcyBkZXNlcmlhbGl6ZWQgdG8gYQogICAgICogcHJvcGVydHkgbmFtZWQgJ2Zvb0JhcicuCiAgICAgKgogICAgICogKiBgcmVhZE9ubHlgOiB7Ym9vbGVhbn0gY3JlYXRlcyBhIHJlYWRPbmx5IHByb3BlcnR5IGFuZAogICAgICogbWFrZXMgYSBwcml2YXRlIHNldHRlciBmb3IgdGhlIHByaXZhdGUgb2YgdGhlIGZvcm0gJ19zZXRGb28nIGZvciBhCiAgICAgKiBwcm9wZXJ0eSAnZm9vJywKICAgICAqCiAgICAgKiAqIGBjb21wdXRlZGA6IHtzdHJpbmd9IGNyZWF0ZXMgYSBjb21wdXRlZCBwcm9wZXJ0eS4gQSBjb21wdXRlZCBwcm9wZXJ0eQogICAgICogaXMgYWxzbyBhdXRvbWF0aWNhbGx5IHNldCB0byBgcmVhZE9ubHk6IHRydWVgLiBUaGUgdmFsdWUgaXMgY2FsY3VsYXRlZAogICAgICogYnkgcnVubmluZyBhIG1ldGhvZCBhbmQgYXJndW1lbnRzIHBhcnNlZCBmcm9tIHRoZSBnaXZlbiBzdHJpbmcuIEZvcgogICAgICogZXhhbXBsZSAnY29tcHV0ZShmb28pJyB3aWxsIGNvbXB1dGUgYSBnaXZlbiBwcm9wZXJ0eSB3aGVuIHRoZQogICAgICogJ2ZvbycgcHJvcGVydHkgY2hhbmdlcyBieSBleGVjdXRpbmcgdGhlICdjb21wdXRlJyBtZXRob2QuIFRoaXMgbWV0aG9kCiAgICAgKiBtdXN0IHJldHVybiB0aGUgY29tcHV0ZWQgdmFsdWUuCiAgICAgKgogICAgICogKiBgcmVmbGVjdFRvQXR0cmlidXRlYDoge2Jvb2xlYW59IElmIHRydWUsIHRoZSBwcm9wZXJ0eSB2YWx1ZSBpcyByZWZsZWN0ZWQKICAgICAqIHRvIGFuIGF0dHJpYnV0ZSBvZiB0aGUgc2FtZSBuYW1lLiBOb3RlLCB0aGUgYXR0cmlidXRlIGlzIGRhc2gtY2FzZWQKICAgICAqIHNvIGEgcHJvcGVydHkgbmFtZWQgJ2Zvb0JhcicgaXMgcmVmbGVjdGVkIGFzICdmb28tYmFyJy4KICAgICAqCiAgICAgKiAqIGBub3RpZnlgOiB7Ym9vbGVhbn0gc2VuZHMgYSBub24tYnViYmxpbmcgbm90aWZpY2F0aW9uIGV2ZW50IHdoZW4KICAgICAqIHRoZSBwcm9wZXJ0eSBjaGFuZ2VzLiBGb3IgZXhhbXBsZSwgYSBwcm9wZXJ0eSBuYW1lZCAnZm9vJyBzZW5kcyBhbgogICAgICogZXZlbnQgbmFtZWQgJ2Zvby1jaGFuZ2VkJyB3aXRoIGBldmVudC5kZXRhaWxgIHNldCB0byB0aGUgdmFsdWUgb2YKICAgICAqIHRoZSBwcm9wZXJ0eS4KICAgICAqCiAgICAgKiAqIG9ic2VydmVyOiB7c3RyaW5nfSBuYW1lIG9mIGEgbWV0aG9kIHRoYXQgcnVucyB3aGVuIHRoZSBwcm9wZXJ0eQogICAgICogY2hhbmdlcy4gVGhlIGFyZ3VtZW50cyBvZiB0aGUgbWV0aG9kIGFyZSAodmFsdWUsIHByZXZpb3VzVmFsdWUpLgogICAgICoKICAgICAqIE5vdGU6IFVzZXJzIG1heSB3YW50IGNvbnRyb2wgb3ZlciBtb2RpZnlpbmcgcHJvcGVydHkKICAgICAqIGVmZmVjdHMgdmlhIHN1YmNsYXNzaW5nLiBGb3IgZXhhbXBsZSwgYSB1c2VyIG1pZ2h0IHdhbnQgdG8gbWFrZSBhCiAgICAgKiByZWZsZWN0VG9BdHRyaWJ1dGUgcHJvcGVydHkgbm90IGRvIHNvIGluIGEgc3ViY2xhc3MuIFdlJ3ZlIGNob3NlbiB0bwogICAgICogZGlzYWJsZSB0aGlzIGJlY2F1c2UgaXQgbGVhZHMgdG8gYWRkaXRpb25hbCBjb21wbGljYXRpb24uCiAgICAgKiBGb3IgZXhhbXBsZSwgYSByZWFkT25seSBlZmZlY3QgZ2VuZXJhdGVzIGEgc3BlY2lhbCBzZXR0ZXIuIElmIGEgc3ViY2xhc3MKICAgICAqIGRpc2FibGVzIHRoZSBlZmZlY3QsIHRoZSBzZXR0ZXIgd291bGQgZmFpbCB1bmV4cGVjdGVkbHkuCiAgICAgKiBCYXNlZCBvbiBmZWVkYmFjaywgd2UgbWF5IHdhbnQgdG8gdHJ5IHRvIG1ha2UgZWZmZWN0cyBtb3JlIG1hbGxlYWJsZQogICAgICogYW5kL29yIHByb3ZpZGUgYW4gYWR2YW5jZWQgYXBpIGZvciBtYW5pcHVsYXRpbmcgdGhlbS4KICAgICAqIEFsc28gY29uc2lkZXIgYWRkaW5nIHdhcm5pbmdzIHdoZW4gYW4gZWZmZWN0IGNhbm5vdCBiZSBjaGFuZ2VkLgogICAgICoKICAgICAqIEBwYXJhbSB7IVBvbHltZXJFbGVtZW50fSBwcm90byBFbGVtZW50IGNsYXNzIHByb3RvdHlwZSB0byBhZGQgYWNjZXNzb3JzCiAgICAgKiAgIGFuZCBlZmZlY3RzIHRvCiAgICAgKiBAcGFyYW0ge3N0cmluZ30gbmFtZSBOYW1lIG9mIHRoZSBwcm9wZXJ0eS4KICAgICAqIEBwYXJhbSB7T2JqZWN0fSBpbmZvIEluZm8gb2JqZWN0IGZyb20gd2hpY2ggdG8gY3JlYXRlIHByb3BlcnR5IGVmZmVjdHMuCiAgICAgKiBTdXBwb3J0ZWQga2V5czoKICAgICAqIEBwYXJhbSB7T2JqZWN0fSBhbGxQcm9wcyBGbGF0dGVuZWQgbWFwIG9mIGFsbCBwcm9wZXJ0aWVzIGRlZmluZWQgaW4gdGhpcwogICAgICogICBlbGVtZW50IChpbmNsdWRpbmcgaW5oZXJpdGVkIHByb3BlcnRpZXMpCiAgICAgKiBAcmV0dXJuIHt2b2lkfQogICAgICogQHByaXZhdGUKICAgICAqLwogICAgZnVuY3Rpb24gY3JlYXRlUHJvcGVydHlGcm9tQ29uZmlnKHByb3RvLCBuYW1lLCBpbmZvLCBhbGxQcm9wcykgewogICAgICAvLyBjb21wdXRlZCBmb3JjZXMgcmVhZE9ubHkuLi4KICAgICAgaWYgKGluZm8uY29tcHV0ZWQpIHsKICAgICAgICBpbmZvLnJlYWRPbmx5ID0gdHJ1ZTsKICAgICAgfQogICAgICAvLyBOb3RlLCBzaW5jZSBhbGwgY29tcHV0ZWQgcHJvcGVydGllcyBhcmUgcmVhZE9ubHksIHRoaXMgcHJldmVudHMKICAgICAgLy8gYWRkaW5nIGFkZGl0aW9uYWwgY29tcHV0ZWQgcHJvcGVydHkgZWZmZWN0cyAod2hpY2ggbGVhZHMgdG8gYSBjb25mdXNpbmcKICAgICAgLy8gc2V0dXAgd2hlcmUgbXVsdGlwbGUgdHJpZ2dlcnMgZm9yIHNldHRpbmcgYSBwcm9wZXJ0eSkKICAgICAgLy8gV2hpbGUgd2UgZG8gaGF2ZSBgaGFzQ29tcHV0ZWRFZmZlY3RgIHRoaXMgaXMgc2V0IG9uIHRoZSBwcm9wZXJ0eSdzCiAgICAgIC8vIGRlcGVuZGVuY2llcyByYXRoZXIgdGhhbiBpdHNlbGYuCiAgICAgIGlmIChpbmZvLmNvbXB1dGVkICYmICFwcm90by5faGFzUmVhZE9ubHlFZmZlY3QobmFtZSkpIHsKICAgICAgICBwcm90by5fY3JlYXRlQ29tcHV0ZWRQcm9wZXJ0eShuYW1lLCBpbmZvLmNvbXB1dGVkLCBhbGxQcm9wcyk7CiAgICAgIH0KICAgICAgaWYgKGluZm8ucmVhZE9ubHkgJiYgIXByb3RvLl9oYXNSZWFkT25seUVmZmVjdChuYW1lKSkgewogICAgICAgIHByb3RvLl9jcmVhdGVSZWFkT25seVByb3BlcnR5KG5hbWUsICFpbmZvLmNvbXB1dGVkKTsKICAgICAgfQogICAgICBpZiAoaW5mby5yZWZsZWN0VG9BdHRyaWJ1dGUgJiYgIXByb3RvLl9oYXNSZWZsZWN0RWZmZWN0KG5hbWUpKSB7CiAgICAgICAgcHJvdG8uX2NyZWF0ZVJlZmxlY3RlZFByb3BlcnR5KG5hbWUpOwogICAgICB9CiAgICAgIGlmIChpbmZvLm5vdGlmeSAmJiAhcHJvdG8uX2hhc05vdGlmeUVmZmVjdChuYW1lKSkgewogICAgICAgIHByb3RvLl9jcmVhdGVOb3RpZnlpbmdQcm9wZXJ0eShuYW1lKTsKICAgICAgfQogICAgICAvLyBhbHdheXMgYWRkIG9ic2VydmVyCiAgICAgIGlmIChpbmZvLm9ic2VydmVyKSB7CiAgICAgICAgcHJvdG8uX2NyZWF0ZVByb3BlcnR5T2JzZXJ2ZXIobmFtZSwgaW5mby5vYnNlcnZlciwgYWxsUHJvcHNbaW5mby5vYnNlcnZlcl0pOwogICAgICB9CiAgICAgIC8vIGFsd2F5cyBjcmVhdGUgdGhlIG1hcHBpbmcgZnJvbSBhdHRyaWJ1dGUgYmFjayB0byBwcm9wZXJ0eSBmb3IgZGVzZXJpYWxpemF0aW9uLgogICAgICBwcm90by5fYWRkUHJvcGVydHlUb0F0dHJpYnV0ZU1hcChuYW1lKTsKICAgIH0KCiAgICAvKioKICAgICAqIFByb2Nlc3MgYWxsIHN0eWxlIGVsZW1lbnRzIGluIHRoZSBlbGVtZW50IHRlbXBsYXRlLiBTdHlsZXMgd2l0aCB0aGUKICAgICAqIGBpbmNsdWRlYCBhdHRyaWJ1dGUgYXJlIHByb2Nlc3NlZCBzdWNoIHRoYXQgYW55IHN0eWxlcyBpbgogICAgICogdGhlIGFzc29jaWF0ZWQgInN0eWxlIG1vZHVsZXMiIGFyZSBpbmNsdWRlZCBpbiB0aGUgZWxlbWVudCB0ZW1wbGF0ZS4KICAgICAqIEBwYXJhbSB7UG9seW1lckVsZW1lbnRDb25zdHJ1Y3Rvcn0ga2xhc3MgRWxlbWVudCBjbGFzcwogICAgICogQHBhcmFtIHshSFRNTFRlbXBsYXRlRWxlbWVudH0gdGVtcGxhdGUgVGVtcGxhdGUgdG8gcHJvY2VzcwogICAgICogQHBhcmFtIHtzdHJpbmd9IGlzIE5hbWUgb2YgZWxlbWVudAogICAgICogQHBhcmFtIHtzdHJpbmd9IGJhc2VVUkkgQmFzZSBVUkkgZm9yIGVsZW1lbnQKICAgICAqIEBwcml2YXRlCiAgICAgKi8KICAgIGZ1bmN0aW9uIHByb2Nlc3NFbGVtZW50U3R5bGVzKGtsYXNzLCB0ZW1wbGF0ZSwgaXMsIGJhc2VVUkkpIHsKICAgICAgaWYgKCFidWlsdENTUykgewogICAgICAgIGNvbnN0IHRlbXBsYXRlU3R5bGVzID0gdGVtcGxhdGUuY29udGVudC5xdWVyeVNlbGVjdG9yQWxsKCdzdHlsZScpOwogICAgICAgIGNvbnN0IHN0eWxlc1dpdGhJbXBvcnRzID0gUG9seW1lci5TdHlsZUdhdGhlci5zdHlsZXNGcm9tVGVtcGxhdGUodGVtcGxhdGUpOwogICAgICAgIC8vIGluc2VydCBzdHlsZXMgZnJvbSA8bGluayByZWw9ImltcG9ydCIgdHlwZT0iY3NzIj4gYXQgdGhlIHRvcCBvZiB0aGUgdGVtcGxhdGUKICAgICAgICBjb25zdCBsaW5rZWRTdHlsZXMgPSBQb2x5bWVyLlN0eWxlR2F0aGVyLnN0eWxlc0Zyb21Nb2R1bGVJbXBvcnRzKGlzKTsKICAgICAgICBjb25zdCBmaXJzdFRlbXBsYXRlQ2hpbGQgPSB0ZW1wbGF0ZS5jb250ZW50LmZpcnN0RWxlbWVudENoaWxkOwogICAgICAgIGZvciAobGV0IGlkeCA9IDA7IGlkeCA8IGxpbmtlZFN0eWxlcy5sZW5ndGg7IGlkeCsrKSB7CiAgICAgICAgICBsZXQgcyA9IGxpbmtlZFN0eWxlc1tpZHhdOwogICAgICAgICAgcy50ZXh0Q29udGVudCA9IGtsYXNzLl9wcm9jZXNzU3R5bGVUZXh0KHMudGV4dENvbnRlbnQsIGJhc2VVUkkpOwogICAgICAgICAgdGVtcGxhdGUuY29udGVudC5pbnNlcnRCZWZvcmUocywgZmlyc3RUZW1wbGF0ZUNoaWxkKTsKICAgICAgICB9CiAgICAgICAgLy8ga2VlcCB0cmFjayBvZiB0aGUgbGFzdCAiY29uY3JldGUiIHN0eWxlIGluIHRoZSB0ZW1wbGF0ZSB3ZSBoYXZlIGVuY291bnRlcmVkCiAgICAgICAgbGV0IHRlbXBsYXRlU3R5bGVJbmRleCA9IDA7CiAgICAgICAgLy8gZW5zdXJlIGFsbCBnYXRoZXJlZCBzdHlsZXMgYXJlIGFjdHVhbGx5IGluIHRoaXMgdGVtcGxhdGUuCiAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBzdHlsZXNXaXRoSW1wb3J0cy5sZW5ndGg7IGkrKykgewogICAgICAgICAgbGV0IHMgPSBzdHlsZXNXaXRoSW1wb3J0c1tpXTsKICAgICAgICAgIGxldCB0ZW1wbGF0ZVN0eWxlID0gdGVtcGxhdGVTdHlsZXNbdGVtcGxhdGVTdHlsZUluZGV4XTsKICAgICAgICAgIC8vIGlmIHRoZSBzdHlsZSBpcyBub3QgaW4gdGhpcyB0ZW1wbGF0ZSwgaXQncyBiZWVuICJpbmNsdWRlZCIgYW5kCiAgICAgICAgICAvLyB3ZSBwdXQgYSBjbG9uZSBvZiBpdCBpbiB0aGUgdGVtcGxhdGUgYmVmb3JlIHRoZSBzdHlsZSB0aGF0IGluY2x1ZGVkIGl0CiAgICAgICAgICBpZiAodGVtcGxhdGVTdHlsZSAhPT0gcykgewogICAgICAgICAgICBzID0gcy5jbG9uZU5vZGUodHJ1ZSk7CiAgICAgICAgICAgIHRlbXBsYXRlU3R5bGUucGFyZW50Tm9kZS5pbnNlcnRCZWZvcmUocywgdGVtcGxhdGVTdHlsZSk7CiAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICB0ZW1wbGF0ZVN0eWxlSW5kZXgrKzsKICAgICAgICAgIH0KICAgICAgICAgIHMudGV4dENvbnRlbnQgPSBrbGFzcy5fcHJvY2Vzc1N0eWxlVGV4dChzLnRleHRDb250ZW50LCBiYXNlVVJJKTsKICAgICAgICB9CiAgICAgIH0KICAgICAgaWYgKHdpbmRvdy5TaGFkeUNTUykgewogICAgICAgIHdpbmRvdy5TaGFkeUNTUy5wcmVwYXJlVGVtcGxhdGUodGVtcGxhdGUsIGlzKTsKICAgICAgfQogICAgfQoKICAgIC8qKgogICAgICogTG9vayB1cCB0ZW1wbGF0ZSBmcm9tIGRvbS1tb2R1bGUgZm9yIGVsZW1lbnQKICAgICAqCiAgICAgKiBAcGFyYW0geyFzdHJpbmd9IGlzIEVsZW1lbnQgbmFtZSB0byBsb29rIHVwCiAgICAgKiBAcmV0dXJuIHshSFRNTFRlbXBsYXRlRWxlbWVudH0gVGVtcGxhdGUgZm91bmQgaW4gZG9tIG1vZHVsZSwgb3IKICAgICAqICAgdW5kZWZpbmVkIGlmIG5vdCBmb3VuZAogICAgICogQHByb3RlY3RlZAogICAgICovCiAgICBmdW5jdGlvbiBnZXRUZW1wbGF0ZUZyb21Eb21Nb2R1bGUoaXMpIHsKICAgICAgbGV0IHRlbXBsYXRlID0gbnVsbDsKICAgICAgaWYgKGlzICYmIFBvbHltZXIuRG9tTW9kdWxlKSB7CiAgICAgICAgdGVtcGxhdGUgPSBQb2x5bWVyLkRvbU1vZHVsZS5pbXBvcnQoaXMsICd0ZW1wbGF0ZScpOwogICAgICAgIC8vIFVuZGVyIHN0cmljdFRlbXBsYXRlUG9saWN5LCByZXF1aXJlIGFueSBlbGVtZW50IHdpdGggYW4gYGlzYAogICAgICAgIC8vIHNwZWNpZmllZCB0byBoYXZlIGEgZG9tLW1vZHVsZQogICAgICAgIGlmIChQb2x5bWVyLnN0cmljdFRlbXBsYXRlUG9saWN5ICYmICF0ZW1wbGF0ZSkgewogICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBzdHJpY3RUZW1wbGF0ZVBvbGljeTogZXhwZWN0aW5nIGRvbS1tb2R1bGUgb3IgbnVsbCB0ZW1wbGF0ZSBmb3IgJHtpc31gKTsKICAgICAgICB9CiAgICAgIH0KICAgICAgcmV0dXJuIHRlbXBsYXRlOwogICAgfQoKICAvKioKICAgICAqIEBwb2x5bWVyCiAgICAgKiBAbWl4aW5DbGFzcwogICAgICogQHVucmVzdHJpY3RlZAogICAgICogQGltcGxlbWVudHMge1BvbHltZXJfRWxlbWVudE1peGlufQogICAgICovCiAgICBjbGFzcyBQb2x5bWVyRWxlbWVudCBleHRlbmRzIHBvbHltZXJFbGVtZW50QmFzZSB7CgogICAgICAvKioKICAgICAgICogT3ZlcnJpZGUgb2YgUHJvcGVydGllc01peGluIF9maW5hbGl6ZUNsYXNzIHRvIGNyZWF0ZSBvYnNlcnZlcnMgYW5kCiAgICAgICAqIGZpbmQgdGhlIHRlbXBsYXRlLgogICAgICAgKiBAcmV0dXJuIHt2b2lkfQogICAgICAgKiBAcHJvdGVjdGVkCiAgICAgICAqIEBvdmVycmlkZQogICAgICAgKiBAc3VwcHJlc3Mge21pc3NpbmdQcm9wZXJ0aWVzfSBJbnRlcmZhY2VzIGluIGNsb3N1cmUgZG8gbm90IGluaGVyaXQgc3RhdGljcywgYnV0IGNsYXNzZXMgZG8KICAgICAgICovCiAgICAgIHN0YXRpYyBfZmluYWxpemVDbGFzcygpIHsKICAgICAgICBzdXBlci5fZmluYWxpemVDbGFzcygpOwogICAgICAgIGNvbnN0IG9ic2VydmVycyA9IG93bk9ic2VydmVycyh0aGlzKTsKICAgICAgICBpZiAob2JzZXJ2ZXJzKSB7CiAgICAgICAgICB0aGlzLmNyZWF0ZU9ic2VydmVycyhvYnNlcnZlcnMsIHRoaXMuX3Byb3BlcnRpZXMpOwogICAgICAgIH0KICAgICAgICB0aGlzLl9wcmVwYXJlVGVtcGxhdGUoKTsKICAgICAgfQoKICAgICAgc3RhdGljIF9wcmVwYXJlVGVtcGxhdGUoKSB7CiAgICAgICAgLy8gbm90ZTogY3JlYXRlICJ3b3JraW5nIiB0ZW1wbGF0ZSB0aGF0IGlzIGZpbmFsaXplZCBhdCBpbnN0YW5jZSB0aW1lCiAgICAgICAgbGV0IHRlbXBsYXRlID0gLyoqIEB0eXBlIHtQb2x5bWVyRWxlbWVudENvbnN0cnVjdG9yfSAqLyAodGhpcykudGVtcGxhdGU7CiAgICAgICAgaWYgKHRlbXBsYXRlKSB7CiAgICAgICAgICBpZiAodHlwZW9mIHRlbXBsYXRlID09PSAnc3RyaW5nJykgewogICAgICAgICAgICBsZXQgdCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3RlbXBsYXRlJyk7CiAgICAgICAgICAgIHQuaW5uZXJIVE1MID0gdGVtcGxhdGU7CiAgICAgICAgICAgIHRlbXBsYXRlID0gdDsKICAgICAgICAgIH0gZWxzZSBpZiAoIVBvbHltZXIubGVnYWN5T3B0aW1pemF0aW9ucykgewogICAgICAgICAgICAgdGVtcGxhdGUgPSB0ZW1wbGF0ZS5jbG9uZU5vZGUodHJ1ZSk7CiAgICAgICAgICB9CiAgICAgICAgfQoKICAgICAgICB0aGlzLnByb3RvdHlwZS5fdGVtcGxhdGUgPSB0ZW1wbGF0ZTsKICAgICAgfQoKICAgICAgLyoqCiAgICAgICAqIE92ZXJyaWRlIG9mIFByb3BlcnRpZXNDaGFuZ2VkIGNyZWF0ZVByb3BlcnRpZXMgdG8gY3JlYXRlIGFjY2Vzc29ycwogICAgICAgKiBhbmQgcHJvcGVydHkgZWZmZWN0cyBmb3IgYWxsIG9mIHRoZSBwcm9wZXJ0aWVzLgogICAgICAgKiBAcmV0dXJuIHt2b2lkfQogICAgICAgKiBAcHJvdGVjdGVkCiAgICAgICAqIEBvdmVycmlkZQogICAgICAgKi8KICAgICAgc3RhdGljIGNyZWF0ZVByb3BlcnRpZXMocHJvcHMpIHsKICAgICAgICBmb3IgKGxldCBwIGluIHByb3BzKSB7CiAgICAgICAgICBjcmVhdGVQcm9wZXJ0eUZyb21Db25maWcodGhpcy5wcm90b3R5cGUsIHAsIHByb3BzW3BdLCBwcm9wcyk7CiAgICAgICAgfQogICAgICB9CgogICAgICAvKioKICAgICAgICogQ3JlYXRlcyBvYnNlcnZlcnMgZm9yIHRoZSBnaXZlbiBgb2JzZXJ2ZXJzYCBhcnJheS4KICAgICAgICogTGV2ZXJhZ2VzIGBQcm9wZXJ0eUVmZmVjdHNgIHRvIGNyZWF0ZSBvYnNlcnZlcnMuCiAgICAgICAqIEBwYXJhbSB7T2JqZWN0fSBvYnNlcnZlcnMgQXJyYXkgb2Ygb2JzZXJ2ZXIgZGVzY3JpcHRvcnMgZm9yCiAgICAgICAqICAgdGhpcyBjbGFzcwogICAgICAgKiBAcGFyYW0ge09iamVjdH0gZHluYW1pY0ZucyBPYmplY3QgY29udGFpbmluZyBrZXlzIGZvciBhbnkgcHJvcGVydGllcwogICAgICAgKiAgIHRoYXQgYXJlIGZ1bmN0aW9ucyBhbmQgc2hvdWxkIHRyaWdnZXIgdGhlIGVmZmVjdCB3aGVuIHRoZSBmdW5jdGlvbgogICAgICAgKiAgIHJlZmVyZW5jZSBpcyBjaGFuZ2VkCiAgICAgICAqIEByZXR1cm4ge3ZvaWR9CiAgICAgICAqIEBwcm90ZWN0ZWQKICAgICAgICovCiAgICAgIHN0YXRpYyBjcmVhdGVPYnNlcnZlcnMob2JzZXJ2ZXJzLCBkeW5hbWljRm5zKSB7CiAgICAgICAgY29uc3QgcHJvdG8gPSB0aGlzLnByb3RvdHlwZTsKICAgICAgICBmb3IgKGxldCBpPTA7IGkgPCBvYnNlcnZlcnMubGVuZ3RoOyBpKyspIHsKICAgICAgICAgIHByb3RvLl9jcmVhdGVNZXRob2RPYnNlcnZlcihvYnNlcnZlcnNbaV0sIGR5bmFtaWNGbnMpOwogICAgICAgIH0KICAgICAgfQoKICAgICAgLyoqCiAgICAgICAqIFJldHVybnMgdGhlIHRlbXBsYXRlIHRoYXQgd2lsbCBiZSBzdGFtcGVkIGludG8gdGhpcyBlbGVtZW50J3Mgc2hhZG93IHJvb3QuCiAgICAgICAqCiAgICAgICAqIElmIGEgYHN0YXRpYyBnZXQgaXMoKWAgZ2V0dGVyIGlzIGRlZmluZWQsIHRoZSBkZWZhdWx0IGltcGxlbWVudGF0aW9uCiAgICAgICAqIHdpbGwgcmV0dXJuIHRoZSBmaXJzdCBgPHRlbXBsYXRlPmAgaW4gYSBgZG9tLW1vZHVsZWAgd2hvc2UgYGlkYAogICAgICAgKiBtYXRjaGVzIHRoaXMgZWxlbWVudCdzIGBpc2AuCiAgICAgICAqCiAgICAgICAqIFVzZXJzIG1heSBvdmVycmlkZSB0aGlzIGdldHRlciB0byByZXR1cm4gYW4gYXJiaXRyYXJ5IHRlbXBsYXRlCiAgICAgICAqIChpbiB3aGljaCBjYXNlIHRoZSBgaXNgIGdldHRlciBpcyB1bm5lY2Vzc2FyeSkuIFRoZSB0ZW1wbGF0ZSByZXR1cm5lZAogICAgICAgKiBtYXkgYmUgZWl0aGVyIGFuIGBIVE1MVGVtcGxhdGVFbGVtZW50YCBvciBhIHN0cmluZyB0aGF0IHdpbGwgYmUKICAgICAgICogYXV0b21hdGljYWxseSBwYXJzZWQgaW50byBhIHRlbXBsYXRlLgogICAgICAgKgogICAgICAgKiBOb3RlIHRoYXQgd2hlbiBzdWJjbGFzc2luZywgaWYgdGhlIHN1cGVyIGNsYXNzIG92ZXJyb2RlIHRoZSBkZWZhdWx0CiAgICAgICAqIGltcGxlbWVudGF0aW9uIGFuZCB0aGUgc3ViY2xhc3Mgd291bGQgbGlrZSB0byBwcm92aWRlIGFuIGFsdGVybmF0ZQogICAgICAgKiB0ZW1wbGF0ZSB2aWEgYSBgZG9tLW1vZHVsZWAsIGl0IHNob3VsZCBvdmVycmlkZSB0aGlzIGdldHRlciBhbmQKICAgICAgICogcmV0dXJuIGBQb2x5bWVyLkRvbU1vZHVsZS5pbXBvcnQodGhpcy5pcywgJ3RlbXBsYXRlJylgLgogICAgICAgKgogICAgICAgKiBJZiBhIHN1YmNsYXNzIHdvdWxkIGxpa2UgdG8gbW9kaWZ5IHRoZSBzdXBlciBjbGFzcyB0ZW1wbGF0ZSwgaXQgc2hvdWxkCiAgICAgICAqIGNsb25lIGl0IHJhdGhlciB0aGFuIG1vZGlmeSBpdCBpbiBwbGFjZS4gIElmIHRoZSBnZXR0ZXIgZG9lcyBleHBlbnNpdmUKICAgICAgICogd29yayBzdWNoIGFzIGNsb25pbmcvbW9kaWZ5aW5nIGEgdGVtcGxhdGUsIGl0IHNob3VsZCBtZW1vaXplIHRoZQogICAgICAgKiB0ZW1wbGF0ZSBmb3IgbWF4aW11bSBwZXJmb3JtYW5jZToKICAgICAgICoKICAgICAgICogICBsZXQgbWVtb2l6ZWRUZW1wbGF0ZTsKICAgICAgICogICBjbGFzcyBNeVN1YkNsYXNzIGV4dGVuZHMgTXlTdXBlckNsYXNzIHsKICAgICAgICogICAgIHN0YXRpYyBnZXQgdGVtcGxhdGUoKSB7CiAgICAgICAqICAgICAgIGlmICghbWVtb2l6ZWRUZW1wbGF0ZSkgewogICAgICAgKiAgICAgICAgIG1lbW9pemVkVGVtcGxhdGUgPSBNeVN1cGVyQ2xhc3MudGVtcGxhdGUuY2xvbmVOb2RlKHRydWUpOwogICAgICAgKiAgICAgICAgIGxldCBzdWJDb250ZW50ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7CiAgICAgICAqICAgICAgICAgc3ViQ29udGVudC50ZXh0Q29udGVudCA9ICdUaGlzIGNhbWUgZnJvbSBNeVN1YkNsYXNzJzsKICAgICAgICogICAgICAgICBtZW1vaXplZFRlbXBsYXRlLmNvbnRlbnQuYXBwZW5kQ2hpbGQoc3ViQ29udGVudCk7CiAgICAgICAqICAgICAgIH0KICAgICAgICogICAgICAgcmV0dXJuIG1lbW9pemVkVGVtcGxhdGU7CiAgICAgICAqICAgICB9CiAgICAgICAqICAgfQogICAgICAgKgogICAgICAgKiBAcmV0dXJuIHtIVE1MVGVtcGxhdGVFbGVtZW50fHN0cmluZ30gVGVtcGxhdGUgdG8gYmUgc3RhbXBlZAogICAgICAgKi8KICAgICAgc3RhdGljIGdldCB0ZW1wbGF0ZSgpIHsKICAgICAgICAvLyBFeHBsYW5hdGlvbiBvZiB0ZW1wbGF0ZS1yZWxhdGVkIHByb3BlcnRpZXM6CiAgICAgICAgLy8gLSBjb25zdHJ1Y3Rvci50ZW1wbGF0ZSAodGhpcyBnZXR0ZXIpOiB0aGUgdGVtcGxhdGUgZm9yIHRoZSBjbGFzcy4KICAgICAgICAvLyAgICAgVGhpcyBjYW4gY29tZSBmcm9tIHRoZSBwcm90b3R5cGUgKGZvciBsZWdhY3kgZWxlbWVudHMpLCBmcm9tIGEKICAgICAgICAvLyAgICAgZG9tLW1vZHVsZSwgb3IgZnJvbSB0aGUgc3VwZXIgY2xhc3MncyB0ZW1wbGF0ZSAob3IgY2FuIGJlIG92ZXJyaWRkZW4KICAgICAgICAvLyAgICAgYWx0b2dldGhlciBieSB0aGUgdXNlcikKICAgICAgICAvLyAtIGNvbnN0cnVjdG9yLl90ZW1wbGF0ZTogbWVtb2l6ZWQgdmVyc2lvbiBvZiBjb25zdHJ1Y3Rvci50ZW1wbGF0ZQogICAgICAgIC8vIC0gcHJvdG90eXBlLl90ZW1wbGF0ZTogd29ya2luZyB0ZW1wbGF0ZSBmb3IgdGhlIGVsZW1lbnQsIHdoaWNoIHdpbGwgYmUKICAgICAgICAvLyAgICAgcGFyc2VkIGFuZCBtb2RpZmllZCBpbiBwbGFjZS4gSXQgaXMgYSBjbG9uZWQgdmVyc2lvbiBvZgogICAgICAgIC8vICAgICBjb25zdHJ1Y3Rvci50ZW1wbGF0ZSwgc2F2ZWQgaW4gX2ZpbmFsaXplQ2xhc3MoKS4gTm90ZSB0aGF0IGJlZm9yZQogICAgICAgIC8vICAgICB0aGlzIGdldHRlciBpcyBjYWxsZWQsIGZvciBsZWdhY3kgZWxlbWVudHMgdGhpcyBjb3VsZCBiZSBmcm9tIGEKICAgICAgICAvLyAgICAgX3RlbXBsYXRlIGZpZWxkIG9uIHRoZSBpbmZvIG9iamVjdCBwYXNzZWQgdG8gUG9seW1lcigpLCBhIGJlaGF2aW9yLAogICAgICAgIC8vICAgICBvciBzZXQgaW4gcmVnaXN0ZXJlZCgpOyBvbmNlIHRoZSBzdGF0aWMgZ2V0dGVyIHJ1bnMsIGEgY2xvbmUgb2YgaXQKICAgICAgICAvLyAgICAgd2lsbCBvdmVyd3JpdGUgaXQgb24gdGhlIHByb3RvdHlwZSBhcyB0aGUgd29ya2luZyB0ZW1wbGF0ZS4KICAgICAgICBpZiAoIXRoaXMuaGFzT3duUHJvcGVydHkoSlNDb21waWxlcl9yZW5hbWVQcm9wZXJ0eSgnX3RlbXBsYXRlJywgdGhpcykpKSB7CiAgICAgICAgICB0aGlzLl90ZW1wbGF0ZSA9CiAgICAgICAgICAgIC8vIElmIHVzZXIgaGFzIHB1dCB0ZW1wbGF0ZSBvbiBwcm90b3R5cGUgKGUuZy4gaW4gbGVnYWN5IHZpYSByZWdpc3RlcmVkCiAgICAgICAgICAgIC8vIGNhbGxiYWNrIG9yIGluZm8gb2JqZWN0KSwgcHJlZmVyIHRoYXQgZmlyc3QKICAgICAgICAgICAgdGhpcy5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkoSlNDb21waWxlcl9yZW5hbWVQcm9wZXJ0eSgnX3RlbXBsYXRlJywgdGhpcy5wcm90b3R5cGUpKSA/CiAgICAgICAgICAgIHRoaXMucHJvdG90eXBlLl90ZW1wbGF0ZSA6CiAgICAgICAgICAgIC8vIExvb2sgaW4gZG9tLW1vZHVsZSBhc3NvY2lhdGVkIHdpdGggdGhpcyBlbGVtZW50J3MgaXMKICAgICAgICAgICAgKGdldFRlbXBsYXRlRnJvbURvbU1vZHVsZSgvKiogQHR5cGUge1BvbHltZXJFbGVtZW50Q29uc3RydWN0b3J9Ki8gKHRoaXMpLmlzKSB8fAogICAgICAgICAgICAvLyBOZXh0IGxvb2sgZm9yIHN1cGVyY2xhc3MgdGVtcGxhdGUgKGNhbGwgdGhlIHN1cGVyIGltcGwgdGhpcwogICAgICAgICAgICAvLyB3YXkgc28gdGhhdCBgdGhpc2AgcG9pbnRzIHRvIHRoZSBzdXBlcmNsYXNzKQogICAgICAgICAgICBPYmplY3QuZ2V0UHJvdG90eXBlT2YoLyoqIEB0eXBlIHtQb2x5bWVyRWxlbWVudENvbnN0cnVjdG9yfSovICh0aGlzKS5wcm90b3R5cGUpLmNvbnN0cnVjdG9yLnRlbXBsYXRlKTsKICAgICAgICB9CiAgICAgICAgcmV0dXJuIHRoaXMuX3RlbXBsYXRlOwogICAgICB9CgogICAgICAvKioKICAgICAgICogU2V0IHRoZSB0ZW1wbGF0ZS4KICAgICAgICoKICAgICAgICogQHBhcmFtIHshSFRNTFRlbXBsYXRlRWxlbWVudHxzdHJpbmd9IHZhbHVlIFRlbXBsYXRlIHRvIHNldC4KICAgICAgICovCiAgICAgIHN0YXRpYyBzZXQgdGVtcGxhdGUodmFsdWUpIHsKICAgICAgICB0aGlzLl90ZW1wbGF0ZSA9IHZhbHVlOwogICAgICB9CgogICAgICAvKioKICAgICAgICogUGF0aCBtYXRjaGluZyB0aGUgdXJsIGZyb20gd2hpY2ggdGhlIGVsZW1lbnQgd2FzIGltcG9ydGVkLgogICAgICAgKgogICAgICAgKiBUaGlzIHBhdGggaXMgdXNlZCB0byByZXNvbHZlIHVybCdzIGluIHRlbXBsYXRlIHN0eWxlIGNzc1RleHQuCiAgICAgICAqIFRoZSBgaW1wb3J0UGF0aGAgcHJvcGVydHkgaXMgYWxzbyBzZXQgb24gZWxlbWVudCBpbnN0YW5jZXMgYW5kIGNhbiBiZQogICAgICAgKiB1c2VkIHRvIGNyZWF0ZSBiaW5kaW5ncyByZWxhdGl2ZSB0byB0aGUgaW1wb3J0IHBhdGguCiAgICAgICAqCiAgICAgICAqIEZvciBlbGVtZW50cyBkZWZpbmVkIGluIEVTIG1vZHVsZXMsIHVzZXJzIHNob3VsZCBpbXBsZW1lbnQKICAgICAgICogYHN0YXRpYyBnZXQgaW1wb3J0TWV0YSgpIHsgcmV0dXJuIGltcG9ydC5tZXRhOyB9YCwgYW5kIHRoZSBkZWZhdWx0CiAgICAgICAqIGltcGxlbWVudGF0aW9uIG9mIGBpbXBvcnRQYXRoYCB3aWxsICByZXR1cm4gYGltcG9ydC5tZXRhLnVybGAncyBwYXRoLgogICAgICAgKiBGb3IgZWxlbWVudHMgZGVmaW5lZCBpbiBIVE1MIGltcG9ydHMsIHRoaXMgZ2V0dGVyIHdpbGwgcmV0dXJuIHRoZSBwYXRoCiAgICAgICAqIHRvIHRoZSBkb2N1bWVudCBjb250YWluaW5nIGEgYGRvbS1tb2R1bGVgIGVsZW1lbnQgbWF0Y2hpbmcgdGhpcwogICAgICAgKiBlbGVtZW50J3Mgc3RhdGljIGBpc2AgcHJvcGVydHkuCiAgICAgICAqCiAgICAgICAqIE5vdGUsIHRoaXMgcGF0aCBzaG91bGQgY29udGFpbiBhIHRyYWlsaW5nIGAvYC4KICAgICAgICoKICAgICAgICogQHJldHVybiB7c3RyaW5nfSBUaGUgaW1wb3J0IHBhdGggZm9yIHRoaXMgZWxlbWVudCBjbGFzcwogICAgICAgKiBAc3VwcHJlc3Mge21pc3NpbmdQcm9wZXJ0aWVzfQogICAgICAgKi8KICAgICAgc3RhdGljIGdldCBpbXBvcnRQYXRoKCkgewogICAgICAgIGlmICghdGhpcy5oYXNPd25Qcm9wZXJ0eShKU0NvbXBpbGVyX3JlbmFtZVByb3BlcnR5KCdfaW1wb3J0UGF0aCcsIHRoaXMpKSkgewogICAgICAgICAgY29uc3QgbWV0YSA9IHRoaXMuaW1wb3J0TWV0YTsKICAgICAgICAgIGlmIChtZXRhKSB7CiAgICAgICAgICAgIHRoaXMuX2ltcG9ydFBhdGggPSBQb2x5bWVyLlJlc29sdmVVcmwucGF0aEZyb21VcmwobWV0YS51cmwpOwogICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgY29uc3QgbW9kdWxlID0gUG9seW1lci5Eb21Nb2R1bGUgJiYgUG9seW1lci5Eb21Nb2R1bGUuaW1wb3J0KC8qKiBAdHlwZSB7UG9seW1lckVsZW1lbnRDb25zdHJ1Y3Rvcn0gKi8gKHRoaXMpLmlzKTsKICAgICAgICAgICAgdGhpcy5faW1wb3J0UGF0aCA9IChtb2R1bGUgJiYgbW9kdWxlLmFzc2V0cGF0aCkgfHwKICAgICAgICAgICAgICBPYmplY3QuZ2V0UHJvdG90eXBlT2YoLyoqIEB0eXBlIHtQb2x5bWVyRWxlbWVudENvbnN0cnVjdG9yfSovICh0aGlzKS5wcm90b3R5cGUpLmNvbnN0cnVjdG9yLmltcG9ydFBhdGg7CiAgICAgICAgICB9CiAgICAgICAgfQogICAgICAgIHJldHVybiB0aGlzLl9pbXBvcnRQYXRoOwogICAgICB9CgogICAgICBjb25zdHJ1Y3RvcigpIHsKICAgICAgICBzdXBlcigpOwogICAgICAgIC8qKiBAdHlwZSB7SFRNTFRlbXBsYXRlRWxlbWVudH0gKi8KICAgICAgICB0aGlzLl90ZW1wbGF0ZTsKICAgICAgICAvKiogQHR5cGUge3N0cmluZ30gKi8KICAgICAgICB0aGlzLl9pbXBvcnRQYXRoOwogICAgICAgIC8qKiBAdHlwZSB7c3RyaW5nfSAqLwogICAgICAgIHRoaXMucm9vdFBhdGg7CiAgICAgICAgLyoqIEB0eXBlIHtzdHJpbmd9ICovCiAgICAgICAgdGhpcy5pbXBvcnRQYXRoOwogICAgICAgIC8qKiBAdHlwZSB7U3RhbXBlZFRlbXBsYXRlIHwgSFRNTEVsZW1lbnQgfCBTaGFkb3dSb290fSAqLwogICAgICAgIHRoaXMucm9vdDsKICAgICAgICAvKiogQHR5cGUgeyFPYmplY3Q8c3RyaW5nLCAhRWxlbWVudD59ICovCiAgICAgICAgdGhpcy4kOwogICAgICB9CgogICAgICAvKioKICAgICAgICogT3ZlcnJpZGVzIHRoZSBkZWZhdWx0IGBQb2x5bWVyLlByb3BlcnR5QWNjZXNzb3JzYCB0byBlbnN1cmUgY2xhc3MKICAgICAgICogbWV0YXByb2dyYW1taW5nIHJlbGF0ZWQgdG8gcHJvcGVydHkgYWNjZXNzb3JzIGFuZCBlZmZlY3RzIGhhcwogICAgICAgKiBjb21wbGV0ZWQgKGNhbGxzIGBmaW5hbGl6ZWApLgogICAgICAgKgogICAgICAgKiBJdCBhbHNvIGluaXRpYWxpemVzIGFueSBwcm9wZXJ0eSBkZWZhdWx0cyBwcm92aWRlZCB2aWEgYHZhbHVlYCBpbgogICAgICAgKiBgcHJvcGVydGllc2AgbWV0YWRhdGEuCiAgICAgICAqCiAgICAgICAqIEByZXR1cm4ge3ZvaWR9CiAgICAgICAqIEBvdmVycmlkZQogICAgICAgKiBAc3VwcHJlc3Mge2ludmFsaWRDYXN0c30KICAgICAgICovCiAgICAgIF9pbml0aWFsaXplUHJvcGVydGllcygpIHsKICAgICAgICB0aGlzLmNvbnN0cnVjdG9yLmZpbmFsaXplKCk7CiAgICAgICAgLy8gbm90ZTogZmluYWxpemUgdGVtcGxhdGUgd2hlbiB3ZSBoYXZlIGFjY2VzcyB0byBgbG9jYWxOYW1lYCB0bwogICAgICAgIC8vIGF2b2lkIGRlcGVuZGVuY2Ugb24gYGlzYCBmb3IgcG9seWZpbGxpbmcgc3R5bGluZy4KICAgICAgICB0aGlzLmNvbnN0cnVjdG9yLl9maW5hbGl6ZVRlbXBsYXRlKC8qKiBAdHlwZSB7IUhUTUxFbGVtZW50fSAqLyh0aGlzKS5sb2NhbE5hbWUpOwogICAgICAgIHN1cGVyLl9pbml0aWFsaXplUHJvcGVydGllcygpOwogICAgICAgIC8vIHNldCBwYXRoIGRlZmF1bHRzCiAgICAgICAgdGhpcy5yb290UGF0aCA9IFBvbHltZXIucm9vdFBhdGg7CiAgICAgICAgdGhpcy5pbXBvcnRQYXRoID0gdGhpcy5jb25zdHJ1Y3Rvci5pbXBvcnRQYXRoOwogICAgICAgIC8vIGFwcGx5IHByb3BlcnR5IGRlZmF1bHRzLi4uCiAgICAgICAgbGV0IHAkID0gcHJvcGVydHlEZWZhdWx0cyh0aGlzLmNvbnN0cnVjdG9yKTsKICAgICAgICBpZiAoIXAkKSB7CiAgICAgICAgICByZXR1cm47CiAgICAgICAgfQogICAgICAgIGZvciAobGV0IHAgaW4gcCQpIHsKICAgICAgICAgIGxldCBpbmZvID0gcCRbcF07CiAgICAgICAgICAvLyBEb24ndCBzZXQgZGVmYXVsdCB2YWx1ZSBpZiB0aGVyZSBpcyBhbHJlYWR5IGFuIG93biBwcm9wZXJ0eSwgd2hpY2gKICAgICAgICAgIC8vIGhhcHBlbnMgd2hlbiBhIGBwcm9wZXJ0aWVzYCBwcm9wZXJ0eSB3aXRoIGRlZmF1bHQgYnV0IG5vIGVmZmVjdHMgaGFkCiAgICAgICAgICAvLyBhIHByb3BlcnR5IHNldCAoZS5nLiBib3VuZCkgYnkgaXRzIGhvc3QgYmVmb3JlIHVwZ3JhZGUKICAgICAgICAgIGlmICghdGhpcy5oYXNPd25Qcm9wZXJ0eShwKSkgewogICAgICAgICAgICBsZXQgdmFsdWUgPSB0eXBlb2YgaW5mby52YWx1ZSA9PSAnZnVuY3Rpb24nID8KICAgICAgICAgICAgICBpbmZvLnZhbHVlLmNhbGwodGhpcykgOgogICAgICAgICAgICAgIGluZm8udmFsdWU7CiAgICAgICAgICAgIC8vIFNldCB2aWEgYF9zZXRQcm9wZXJ0eWAgaWYgdGhlcmUgaXMgYW4gYWNjZXNzb3IsIHRvIGVuYWJsZQogICAgICAgICAgICAvLyBpbml0aWFsaXppbmcgcmVhZE9ubHkgcHJvcGVydHkgZGVmYXVsdHMKICAgICAgICAgICAgaWYgKHRoaXMuX2hhc0FjY2Vzc29yKHApKSB7CiAgICAgICAgICAgICAgdGhpcy5fc2V0UGVuZGluZ1Byb3BlcnR5KHAsIHZhbHVlLCB0cnVlKTsKICAgICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgICB0aGlzW3BdID0gdmFsdWU7CiAgICAgICAgICAgIH0KICAgICAgICAgIH0KICAgICAgICB9CiAgICAgIH0KCiAgICAgIC8qKgogICAgICAgKiBHYXRoZXIgc3R5bGUgdGV4dCBmb3IgYSBzdHlsZSBlbGVtZW50IGluIHRoZSB0ZW1wbGF0ZS4KICAgICAgICoKICAgICAgICogQHBhcmFtIHtzdHJpbmd9IGNzc1RleHQgVGV4dCBjb250YWluaW5nIHN0eWxpbmcgdG8gcHJvY2VzcwogICAgICAgKiBAcGFyYW0ge3N0cmluZ30gYmFzZVVSSSBCYXNlIFVSSSB0byByZWJhc2UgQ1NTIHBhdGhzIGFnYWluc3QKICAgICAgICogQHJldHVybiB7c3RyaW5nfSBUaGUgcHJvY2Vzc2VkIENTUyB0ZXh0CiAgICAgICAqIEBwcm90ZWN0ZWQKICAgICAgICovCiAgICAgIHN0YXRpYyBfcHJvY2Vzc1N0eWxlVGV4dChjc3NUZXh0LCBiYXNlVVJJKSB7CiAgICAgICAgcmV0dXJuIFBvbHltZXIuUmVzb2x2ZVVybC5yZXNvbHZlQ3NzKGNzc1RleHQsIGJhc2VVUkkpOwogICAgICB9CgogICAgICAvKioKICAgICAgKiBDb25maWd1cmVzIGFuIGVsZW1lbnQgYHByb3RvYCB0byBmdW5jdGlvbiB3aXRoIGEgZ2l2ZW4gYHRlbXBsYXRlYC4KICAgICAgKiBUaGUgZWxlbWVudCBuYW1lIGBpc2AgYW5kIGV4dGVuZHMgYGV4dGAgbXVzdCBiZSBzcGVjaWZpZWQgZm9yIFNoYWR5Q1NTCiAgICAgICogc3R5bGUgc2NvcGluZy4KICAgICAgKgogICAgICAqIEBwYXJhbSB7c3RyaW5nfSBpcyBUYWcgbmFtZSAob3IgdHlwZSBleHRlbnNpb24gbmFtZSkgZm9yIHRoaXMgZWxlbWVudAogICAgICAqIEByZXR1cm4ge3ZvaWR9CiAgICAgICogQHByb3RlY3RlZAogICAgICAqLwogICAgICBzdGF0aWMgX2ZpbmFsaXplVGVtcGxhdGUoaXMpIHsKICAgICAgICAvKiogQGNvbnN0IHtIVE1MVGVtcGxhdGVFbGVtZW50fSAqLwogICAgICAgIGNvbnN0IHRlbXBsYXRlID0gdGhpcy5wcm90b3R5cGUuX3RlbXBsYXRlOwogICAgICAgIGlmICh0ZW1wbGF0ZSAmJiAhdGVtcGxhdGUuX19wb2x5bWVyRmluYWxpemVkKSB7CiAgICAgICAgICB0ZW1wbGF0ZS5fX3BvbHltZXJGaW5hbGl6ZWQgPSB0cnVlOwogICAgICAgICAgY29uc3QgaW1wb3J0UGF0aCA9IHRoaXMuaW1wb3J0UGF0aDsKICAgICAgICAgIGNvbnN0IGJhc2VVUkkgPSBpbXBvcnRQYXRoID8gUG9seW1lci5SZXNvbHZlVXJsLnJlc29sdmVVcmwoaW1wb3J0UGF0aCkgOiAnJzsKICAgICAgICAgIC8vIGUuZy4gc3VwcG9ydCBgaW5jbHVkZT0ibW9kdWxlLW5hbWUiYCwgYW5kIFNoYWR5Q1NTCiAgICAgICAgICBwcm9jZXNzRWxlbWVudFN0eWxlcyh0aGlzLCB0ZW1wbGF0ZSwgaXMsIGJhc2VVUkkpOwogICAgICAgICAgdGhpcy5wcm90b3R5cGUuX2JpbmRUZW1wbGF0ZSh0ZW1wbGF0ZSk7CiAgICAgICAgfQogICAgICB9CgogICAgICAvKioKICAgICAgICogUHJvdmlkZXMgYSBkZWZhdWx0IGltcGxlbWVudGF0aW9uIG9mIHRoZSBzdGFuZGFyZCBDdXN0b20gRWxlbWVudHMKICAgICAgICogYGNvbm5lY3RlZENhbGxiYWNrYC4KICAgICAgICoKICAgICAgICogVGhlIGRlZmF1bHQgaW1wbGVtZW50YXRpb24gZW5hYmxlcyB0aGUgcHJvcGVydHkgZWZmZWN0cyBzeXN0ZW0gYW5kCiAgICAgICAqIGZsdXNoZXMgYW55IHBlbmRpbmcgcHJvcGVydGllcywgYW5kIHVwZGF0ZXMgc2hpbW1lZCBDU1MgcHJvcGVydGllcwogICAgICAgKiB3aGVuIHVzaW5nIHRoZSBTaGFkeUNTUyBzY29waW5nL2N1c3RvbSBwcm9wZXJ0aWVzIHBvbHlmaWxsLgogICAgICAgKgogICAgICAgKiBAc3VwcHJlc3Mge21pc3NpbmdQcm9wZXJ0aWVzLCBpbnZhbGlkQ2FzdHN9IFN1cGVyIG1heSBvciBtYXkgbm90IGltcGxlbWVudCB0aGUgY2FsbGJhY2sKICAgICAgICogQHJldHVybiB7dm9pZH0KICAgICAgICovCiAgICAgIGNvbm5lY3RlZENhbGxiYWNrKCkgewogICAgICAgIGlmICh3aW5kb3cuU2hhZHlDU1MgJiYgdGhpcy5fdGVtcGxhdGUpIHsKICAgICAgICAgIHdpbmRvdy5TaGFkeUNTUy5zdHlsZUVsZW1lbnQoLyoqIEB0eXBlIHshSFRNTEVsZW1lbnR9ICovKHRoaXMpKTsKICAgICAgICB9CiAgICAgICAgc3VwZXIuY29ubmVjdGVkQ2FsbGJhY2soKTsKICAgICAgfQoKICAgICAgLyoqCiAgICAgICAqIFN0YW1wcyB0aGUgZWxlbWVudCB0ZW1wbGF0ZS4KICAgICAgICoKICAgICAgICogQHJldHVybiB7dm9pZH0KICAgICAgICogQG92ZXJyaWRlCiAgICAgICAqLwogICAgICByZWFkeSgpIHsKICAgICAgICBpZiAodGhpcy5fdGVtcGxhdGUpIHsKICAgICAgICAgIHRoaXMucm9vdCA9IHRoaXMuX3N0YW1wVGVtcGxhdGUodGhpcy5fdGVtcGxhdGUpOwogICAgICAgICAgdGhpcy4kID0gdGhpcy5yb290LiQ7CiAgICAgICAgfQogICAgICAgIHN1cGVyLnJlYWR5KCk7CiAgICAgIH0KCiAgICAgIC8qKgogICAgICAgKiBJbXBsZW1lbnRzIGBQcm9wZXJ0eUVmZmVjdHNgJ3MgYF9yZWFkeUNsaWVudHNgIGNhbGwuIEF0dGFjaGVzCiAgICAgICAqIGVsZW1lbnQgZG9tIGJ5IGNhbGxpbmcgYF9hdHRhY2hEb21gIHdpdGggdGhlIGRvbSBzdGFtcGVkIGZyb20gdGhlCiAgICAgICAqIGVsZW1lbnQncyB0ZW1wbGF0ZSB2aWEgYF9zdGFtcFRlbXBsYXRlYC4gTm90ZSB0aGF0IHRoaXMgYWxsb3dzCiAgICAgICAqIGNsaWVudCBkb20gdG8gYmUgYXR0YWNoZWQgdG8gdGhlIGVsZW1lbnQgcHJpb3IgdG8gYW55IG9ic2VydmVycwogICAgICAgKiBydW5uaW5nLgogICAgICAgKgogICAgICAgKiBAcmV0dXJuIHt2b2lkfQogICAgICAgKiBAb3ZlcnJpZGUKICAgICAgICovCiAgICAgIF9yZWFkeUNsaWVudHMoKSB7CiAgICAgICAgaWYgKHRoaXMuX3RlbXBsYXRlKSB7CiAgICAgICAgICB0aGlzLnJvb3QgPSB0aGlzLl9hdHRhY2hEb20oLyoqIEB0eXBlIHtTdGFtcGVkVGVtcGxhdGV9ICovKHRoaXMucm9vdCkpOwogICAgICAgIH0KICAgICAgICAvLyBUaGUgc3VwZXIuX3JlYWR5Q2xpZW50cyBoZXJlIHNldHMgdGhlIGNsaWVudHMgaW5pdGlhbGl6ZWQgZmxhZy4KICAgICAgICAvLyBXZSBtdXN0IHdhaXQgdG8gZG8gdGhpcyB1bnRpbCBhZnRlciBjbGllbnQgZG9tIGlzIGNyZWF0ZWQvYXR0YWNoZWQKICAgICAgICAvLyBzbyB0aGF0IHRoaXMgZmxhZyBjYW4gYmUgY2hlY2tlZCB0byBwcmV2ZW50IG5vdGlmaWNhdGlvbnMgZmlyZWQKICAgICAgICAvLyBkdXJpbmcgdGhpcyBwcm9jZXNzIGZyb20gYmVpbmcgaGFuZGxlZCBiZWZvcmUgY2xpZW50cyBhcmUgcmVhZHkuCiAgICAgICAgc3VwZXIuX3JlYWR5Q2xpZW50cygpOwogICAgICB9CgoKICAgICAgLyoqCiAgICAgICAqIEF0dGFjaGVzIGFuIGVsZW1lbnQncyBzdGFtcGVkIGRvbSB0byBpdHNlbGYuIEJ5IGRlZmF1bHQsCiAgICAgICAqIHRoaXMgbWV0aG9kIGNyZWF0ZXMgYSBgc2hhZG93Um9vdGAgYW5kIGFkZHMgdGhlIGRvbSB0byBpdC4KICAgICAgICogSG93ZXZlciwgdGhpcyBtZXRob2QgbWF5IGJlIG92ZXJyaWRkZW4gdG8gYWxsb3cgYW4gZWxlbWVudAogICAgICAgKiB0byBwdXQgaXRzIGRvbSBpbiBhbm90aGVyIGxvY2F0aW9uLgogICAgICAgKgogICAgICAgKiBAdGhyb3dzIHtFcnJvcn0KICAgICAgICogQHN1cHByZXNzIHttaXNzaW5nUmV0dXJufQogICAgICAgKiBAcGFyYW0ge1N0YW1wZWRUZW1wbGF0ZX0gZG9tIHRvIGF0dGFjaCB0byB0aGUgZWxlbWVudC4KICAgICAgICogQHJldHVybiB7U2hhZG93Um9vdH0gbm9kZSB0byB3aGljaCB0aGUgZG9tIGhhcyBiZWVuIGF0dGFjaGVkLgogICAgICAgKi8KICAgICAgX2F0dGFjaERvbShkb20pIHsKICAgICAgICBpZiAodGhpcy5hdHRhY2hTaGFkb3cpIHsKICAgICAgICAgIGlmIChkb20pIHsKICAgICAgICAgICAgaWYgKCF0aGlzLnNoYWRvd1Jvb3QpIHsKICAgICAgICAgICAgICB0aGlzLmF0dGFjaFNoYWRvdyh7bW9kZTogJ29wZW4nfSk7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgdGhpcy5zaGFkb3dSb290LmFwcGVuZENoaWxkKGRvbSk7CiAgICAgICAgICAgIHJldHVybiB0aGlzLnNoYWRvd1Jvb3Q7CiAgICAgICAgICB9CiAgICAgICAgICByZXR1cm4gbnVsbDsKICAgICAgICB9IGVsc2UgewogICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdTaGFkb3dET00gbm90IGF2YWlsYWJsZS4gJyArCiAgICAgICAgICAgIC8vIFRPRE8oc29ydmVsbCk6IG1vdmUgdG8gY29tcGlsZS10aW1lIGNvbmRpdGlvbmFsIHdoZW4gc3VwcG9ydGVkCiAgICAgICAgICAnUG9seW1lci5FbGVtZW50IGNhbiBjcmVhdGUgZG9tIGFzIGNoaWxkcmVuIGluc3RlYWQgb2YgaW4gJyArCiAgICAgICAgICAnU2hhZG93RE9NIGJ5IHNldHRpbmcgYHRoaXMucm9vdCA9IHRoaXM7XGAgYmVmb3JlIFxgcmVhZHlcYC4nKTsKICAgICAgICB9CiAgICAgIH0KCiAgICAgIC8qKgogICAgICAgKiBXaGVuIHVzaW5nIHRoZSBTaGFkeUNTUyBzY29waW5nIGFuZCBjdXN0b20gcHJvcGVydHkgc2hpbSwgY2F1c2VzIGFsbAogICAgICAgKiBzaGltbWVkIHN0eWxlcyBpbiB0aGlzIGVsZW1lbnQgKGFuZCBpdHMgc3VidHJlZSkgdG8gYmUgdXBkYXRlZAogICAgICAgKiBiYXNlZCBvbiBjdXJyZW50IGN1c3RvbSBwcm9wZXJ0eSB2YWx1ZXMuCiAgICAgICAqCiAgICAgICAqIFRoZSBvcHRpb25hbCBwYXJhbWV0ZXIgb3ZlcnJpZGVzIGlubGluZSBjdXN0b20gcHJvcGVydHkgc3R5bGVzIHdpdGggYW4KICAgICAgICogb2JqZWN0IG9mIHByb3BlcnRpZXMgd2hlcmUgdGhlIGtleXMgYXJlIENTUyBwcm9wZXJ0aWVzLCBhbmQgdGhlIHZhbHVlcwogICAgICAgKiBhcmUgc3RyaW5ncy4KICAgICAgICoKICAgICAgICogRXhhbXBsZTogYHRoaXMudXBkYXRlU3R5bGVzKHsnLS1jb2xvcic6ICdibHVlJ30pYAogICAgICAgKgogICAgICAgKiBUaGVzZSBwcm9wZXJ0aWVzIGFyZSByZXRhaW5lZCB1bmxlc3MgYSB2YWx1ZSBvZiBgbnVsbGAgaXMgc2V0LgogICAgICAgKgogICAgICAgKiBOb3RlOiBUaGlzIGZ1bmN0aW9uIGRvZXMgbm90IHN1cHBvcnQgdXBkYXRpbmcgQ1NTIG1peGlucy4KICAgICAgICogWW91IGNhbiBub3QgZHluYW1pY2FsbHkgY2hhbmdlIHRoZSB2YWx1ZSBvZiBhbiBgQGFwcGx5YC4KICAgICAgICoKICAgICAgICogQHBhcmFtIHtPYmplY3Q9fSBwcm9wZXJ0aWVzIEJhZyBvZiBjdXN0b20gcHJvcGVydHkga2V5L3ZhbHVlcyB0bwogICAgICAgKiAgIGFwcGx5IHRvIHRoaXMgZWxlbWVudC4KICAgICAgICogQHJldHVybiB7dm9pZH0KICAgICAgICogQHN1cHByZXNzIHtpbnZhbGlkQ2FzdHN9CiAgICAgICAqLwogICAgICB1cGRhdGVTdHlsZXMocHJvcGVydGllcykgewogICAgICAgIGlmICh3aW5kb3cuU2hhZHlDU1MpIHsKICAgICAgICAgIHdpbmRvdy5TaGFkeUNTUy5zdHlsZVN1YnRyZWUoLyoqIEB0eXBlIHshSFRNTEVsZW1lbnR9ICovKHRoaXMpLCBwcm9wZXJ0aWVzKTsKICAgICAgICB9CiAgICAgIH0KCiAgICAgIC8qKgogICAgICAgKiBSZXdyaXRlcyBhIGdpdmVuIFVSTCByZWxhdGl2ZSB0byBhIGJhc2UgVVJMLiBUaGUgYmFzZSBVUkwgZGVmYXVsdHMgdG8KICAgICAgICogdGhlIG9yaWdpbmFsIGxvY2F0aW9uIG9mIHRoZSBkb2N1bWVudCBjb250YWluaW5nIHRoZSBgZG9tLW1vZHVsZWAgZm9yCiAgICAgICAqIHRoaXMgZWxlbWVudC4gVGhpcyBtZXRob2Qgd2lsbCByZXR1cm4gdGhlIHNhbWUgVVJMIGJlZm9yZSBhbmQgYWZ0ZXIKICAgICAgICogYnVuZGxpbmcuCiAgICAgICAqCiAgICAgICAqIE5vdGUgdGhhdCB0aGlzIGZ1bmN0aW9uIHBlcmZvcm1zIG5vIHJlc29sdXRpb24gZm9yIFVSTHMgdGhhdCBzdGFydAogICAgICAgKiB3aXRoIGAvYCAoYWJzb2x1dGUgVVJMcykgb3IgYCNgIChoYXNoIGlkZW50aWZpZXJzKS4gIEZvciBnZW5lcmFsIHB1cnBvc2UKICAgICAgICogVVJMIHJlc29sdXRpb24sIHVzZSBgd2luZG93LlVSTGAuCiAgICAgICAqCiAgICAgICAqIEBwYXJhbSB7c3RyaW5nfSB1cmwgVVJMIHRvIHJlc29sdmUuCiAgICAgICAqIEBwYXJhbSB7c3RyaW5nPX0gYmFzZSBPcHRpb25hbCBiYXNlIFVSTCB0byByZXNvbHZlIGFnYWluc3QsIGRlZmF1bHRzCiAgICAgICAqIHRvIHRoZSBlbGVtZW50J3MgYGltcG9ydFBhdGhgCiAgICAgICAqIEByZXR1cm4ge3N0cmluZ30gUmV3cml0dGVuIFVSTCByZWxhdGl2ZSB0byBiYXNlCiAgICAgICAqLwogICAgICByZXNvbHZlVXJsKHVybCwgYmFzZSkgewogICAgICAgIGlmICghYmFzZSAmJiB0aGlzLmltcG9ydFBhdGgpIHsKICAgICAgICAgIGJhc2UgPSBQb2x5bWVyLlJlc29sdmVVcmwucmVzb2x2ZVVybCh0aGlzLmltcG9ydFBhdGgpOwogICAgICAgIH0KICAgICAgICByZXR1cm4gUG9seW1lci5SZXNvbHZlVXJsLnJlc29sdmVVcmwodXJsLCBiYXNlKTsKICAgICAgfQoKICAgICAgLyoqCiAgICAgICAqIE92ZXJyaWRlcyBgUHJvcGVydHlBY2Nlc3NvcnNgIHRvIGFkZCBtYXAgb2YgZHluYW1pYyBmdW5jdGlvbnMgb24KICAgICAgICogdGVtcGxhdGUgaW5mbywgZm9yIGNvbnN1bXB0aW9uIGJ5IGBQcm9wZXJ0eUVmZmVjdHNgIHRlbXBsYXRlIGJpbmRpbmcKICAgICAgICogY29kZS4gVGhpcyBtYXAgZGV0ZXJtaW5lcyB3aGljaCBtZXRob2QgdGVtcGxhdGVzIHNob3VsZCBoYXZlIGFjY2Vzc29ycwogICAgICAgKiBjcmVhdGVkIGZvciB0aGVtLgogICAgICAgKgogICAgICAgKiBAb3ZlcnJpZGUKICAgICAgICogQHN1cHByZXNzIHttaXNzaW5nUHJvcGVydGllc30gSW50ZXJmYWNlcyBpbiBjbG9zdXJlIGRvIG5vdCBpbmhlcml0IHN0YXRpY3MsIGJ1dCBjbGFzc2VzIGRvCiAgICAgICAqLwogICAgICBzdGF0aWMgX3BhcnNlVGVtcGxhdGVDb250ZW50KHRlbXBsYXRlLCB0ZW1wbGF0ZUluZm8sIG5vZGVJbmZvKSB7CiAgICAgICAgdGVtcGxhdGVJbmZvLmR5bmFtaWNGbnMgPSB0ZW1wbGF0ZUluZm8uZHluYW1pY0ZucyB8fCB0aGlzLl9wcm9wZXJ0aWVzOwogICAgICAgIHJldHVybiBzdXBlci5fcGFyc2VUZW1wbGF0ZUNvbnRlbnQodGVtcGxhdGUsIHRlbXBsYXRlSW5mbywgbm9kZUluZm8pOwogICAgICB9CgogICAgfQoKICAgIHJldHVybiBQb2x5bWVyRWxlbWVudDsKICB9KTsKCiAgLyoqCiAgICogV2hlbiB1c2luZyB0aGUgU2hhZHlDU1Mgc2NvcGluZyBhbmQgY3VzdG9tIHByb3BlcnR5IHNoaW0sIGNhdXNlcyBhbGwKICAgKiBzaGltbWVkIGBzdHlsZXNgICh2aWEgYGN1c3RvbS1zdHlsZWApIGluIHRoZSBkb2N1bWVudCAoYW5kIGl0cyBzdWJ0cmVlKQogICAqIHRvIGJlIHVwZGF0ZWQgYmFzZWQgb24gY3VycmVudCBjdXN0b20gcHJvcGVydHkgdmFsdWVzLgogICAqCiAgICogVGhlIG9wdGlvbmFsIHBhcmFtZXRlciBvdmVycmlkZXMgaW5saW5lIGN1c3RvbSBwcm9wZXJ0eSBzdHlsZXMgd2l0aCBhbgogICAqIG9iamVjdCBvZiBwcm9wZXJ0aWVzIHdoZXJlIHRoZSBrZXlzIGFyZSBDU1MgcHJvcGVydGllcywgYW5kIHRoZSB2YWx1ZXMKICAgKiBhcmUgc3RyaW5ncy4KICAgKgogICAqIEV4YW1wbGU6IGBQb2x5bWVyLnVwZGF0ZVN0eWxlcyh7Jy0tY29sb3InOiAnYmx1ZSd9KWAKICAgKgogICAqIFRoZXNlIHByb3BlcnRpZXMgYXJlIHJldGFpbmVkIHVubGVzcyBhIHZhbHVlIG9mIGBudWxsYCBpcyBzZXQuCiAgICoKICAgKiBAcGFyYW0ge09iamVjdD19IHByb3BzIEJhZyBvZiBjdXN0b20gcHJvcGVydHkga2V5L3ZhbHVlcyB0bwogICAqICAgYXBwbHkgdG8gdGhlIGRvY3VtZW50LgogICAqIEByZXR1cm4ge3ZvaWR9CiAgICovCiAgUG9seW1lci51cGRhdGVTdHlsZXMgPSBmdW5jdGlvbihwcm9wcykgewogICAgaWYgKHdpbmRvdy5TaGFkeUNTUykgewogICAgICB3aW5kb3cuU2hhZHlDU1Muc3R5bGVEb2N1bWVudChwcm9wcyk7CiAgICB9CiAgfTsKCn0pKCk7Cjwvc2NyaXB0PjxzY3JpcHQ+CihmdW5jdGlvbigpIHsKICAndXNlIHN0cmljdCc7CgogIC8qKgogICAqIEBzdW1tYXJ5IENvbGxhcHNlIG11bHRpcGxlIGNhbGxiYWNrcyBpbnRvIG9uZSBpbnZvY2F0aW9uIGFmdGVyIGEgdGltZXIuCiAgICogQG1lbWJlcm9mIFBvbHltZXIKICAgKi8KICBjbGFzcyBEZWJvdW5jZXIgewogICAgY29uc3RydWN0b3IoKSB7CiAgICAgIHRoaXMuX2FzeW5jTW9kdWxlID0gbnVsbDsKICAgICAgdGhpcy5fY2FsbGJhY2sgPSBudWxsOwogICAgICB0aGlzLl90aW1lciA9IG51bGw7CiAgICB9CiAgICAvKioKICAgICAqIFNldHMgdGhlIHNjaGVkdWxlcjsgdGhhdCBpcywgYSBtb2R1bGUgd2l0aCB0aGUgQXN5bmMgaW50ZXJmYWNlLAogICAgICogYSBjYWxsYmFjayBhbmQgb3B0aW9uYWwgYXJndW1lbnRzIHRvIGJlIHBhc3NlZCB0byB0aGUgcnVuIGZ1bmN0aW9uCiAgICAgKiBmcm9tIHRoZSBhc3luYyBtb2R1bGUuCiAgICAgKgogICAgICogQHBhcmFtIHshQXN5bmNJbnRlcmZhY2V9IGFzeW5jTW9kdWxlIE9iamVjdCB3aXRoIEFzeW5jIGludGVyZmFjZS4KICAgICAqIEBwYXJhbSB7ZnVuY3Rpb24oKX0gY2FsbGJhY2sgQ2FsbGJhY2sgdG8gcnVuLgogICAgICogQHJldHVybiB7dm9pZH0KICAgICAqLwogICAgc2V0Q29uZmlnKGFzeW5jTW9kdWxlLCBjYWxsYmFjaykgewogICAgICB0aGlzLl9hc3luY01vZHVsZSA9IGFzeW5jTW9kdWxlOwogICAgICB0aGlzLl9jYWxsYmFjayA9IGNhbGxiYWNrOwogICAgICB0aGlzLl90aW1lciA9IHRoaXMuX2FzeW5jTW9kdWxlLnJ1bigoKSA9PiB7CiAgICAgICAgdGhpcy5fdGltZXIgPSBudWxsOwogICAgICAgIHRoaXMuX2NhbGxiYWNrKCk7CiAgICAgIH0pOwogICAgfQogICAgLyoqCiAgICAgKiBDYW5jZWxzIGFuIGFjdGl2ZSBkZWJvdW5jZXIgYW5kIHJldHVybnMgYSByZWZlcmVuY2UgdG8gaXRzZWxmLgogICAgICoKICAgICAqIEByZXR1cm4ge3ZvaWR9CiAgICAgKi8KICAgIGNhbmNlbCgpIHsKICAgICAgaWYgKHRoaXMuaXNBY3RpdmUoKSkgewogICAgICAgIHRoaXMuX2FzeW5jTW9kdWxlLmNhbmNlbCh0aGlzLl90aW1lcik7CiAgICAgICAgdGhpcy5fdGltZXIgPSBudWxsOwogICAgICB9CiAgICB9CiAgICAvKioKICAgICAqIEZsdXNoZXMgYW4gYWN0aXZlIGRlYm91bmNlciBhbmQgcmV0dXJucyBhIHJlZmVyZW5jZSB0byBpdHNlbGYuCiAgICAgKgogICAgICogQHJldHVybiB7dm9pZH0KICAgICAqLwogICAgZmx1c2goKSB7CiAgICAgIGlmICh0aGlzLmlzQWN0aXZlKCkpIHsKICAgICAgICB0aGlzLmNhbmNlbCgpOwogICAgICAgIHRoaXMuX2NhbGxiYWNrKCk7CiAgICAgIH0KICAgIH0KICAgIC8qKgogICAgICogUmV0dXJucyB0cnVlIGlmIHRoZSBkZWJvdW5jZXIgaXMgYWN0aXZlLgogICAgICoKICAgICAqIEByZXR1cm4ge2Jvb2xlYW59IFRydWUgaWYgYWN0aXZlLgogICAgICovCiAgICBpc0FjdGl2ZSgpIHsKICAgICAgcmV0dXJuIHRoaXMuX3RpbWVyICE9IG51bGw7CiAgICB9CiAgICAvKioKICAgICAqIENyZWF0ZXMgYSBkZWJvdW5jZXIgaWYgbm8gZGVib3VuY2VyIGlzIHBhc3NlZCBhcyBhIHBhcmFtZXRlcgogICAgICogb3IgaXQgY2FuY2VscyBhbiBhY3RpdmUgZGVib3VuY2VyIG90aGVyd2lzZS4gVGhlIGZvbGxvd2luZwogICAgICogZXhhbXBsZSBzaG93cyBob3cgYSBkZWJvdW5jZXIgY2FuIGJlIGNhbGxlZCBtdWx0aXBsZSB0aW1lcyB3aXRoaW4gYQogICAgICogbWljcm90YXNrIGFuZCAiZGVib3VuY2VkIiBzdWNoIHRoYXQgdGhlIHByb3ZpZGVkIGNhbGxiYWNrIGZ1bmN0aW9uIGlzCiAgICAgKiBjYWxsZWQgb25jZS4gQWRkIHRoaXMgbWV0aG9kIHRvIGEgY3VzdG9tIGVsZW1lbnQ6CiAgICAgKgogICAgICogX2RlYm91bmNlV29yaygpIHsKICAgICAqICAgdGhpcy5fZGVib3VuY2VKb2IgPSBQb2x5bWVyLkRlYm91bmNlci5kZWJvdW5jZSh0aGlzLl9kZWJvdW5jZUpvYiwKICAgICAqICAgICAgIFBvbHltZXIuQXN5bmMubWljcm9UYXNrLCAoKSA9PiB7CiAgICAgKiAgICAgdGhpcy5fZG9Xb3JrKCk7CiAgICAgKiAgIH0pOwogICAgICogfQogICAgICoKICAgICAqIElmIHRoZSBgX2RlYm91bmNlV29ya2AgbWV0aG9kIGlzIGNhbGxlZCBtdWx0aXBsZSB0aW1lcyB3aXRoaW4gdGhlIHNhbWUKICAgICAqIG1pY3JvdGFzaywgdGhlIGBfZG9Xb3JrYCBmdW5jdGlvbiB3aWxsIGJlIGNhbGxlZCBvbmx5IG9uY2UgYXQgdGhlIG5leHQKICAgICAqIG1pY3JvdGFzayBjaGVja3BvaW50LgogICAgICoKICAgICAqIE5vdGU6IEluIHRlc3RpbmcgaXQgaXMgb2Z0ZW4gY29udmVuaWVudCB0byBhdm9pZCBhc3luY2hyb255LiBUbyBhY2NvbXBsaXNoCiAgICAgKiB0aGlzIHdpdGggYSBkZWJvdW5jZXIsIHlvdSBjYW4gdXNlIGBQb2x5bWVyLmVucXVldWVEZWJvdW5jZXJgIGFuZAogICAgICogYFBvbHltZXIuZmx1c2hgLiBGb3IgZXhhbXBsZSwgZXh0ZW5kIHRoZSBhYm92ZSBleGFtcGxlIGJ5IGFkZGluZwogICAgICogYFBvbHltZXIuZW5xdWV1ZURlYm91bmNlcih0aGlzLl9kZWJvdW5jZUpvYilgIGF0IHRoZSBlbmQgb2YgdGhlCiAgICAgKiBgX2RlYm91bmNlV29ya2AgbWV0aG9kLiBUaGVuIGluIGEgdGVzdCwgY2FsbCBgUG9seW1lci5mbHVzaGAgdG8gZW5zdXJlCiAgICAgKiB0aGUgZGVib3VuY2VyIGhhcyBjb21wbGV0ZWQuCiAgICAgKgogICAgICogQHBhcmFtIHtEZWJvdW5jZXI/fSBkZWJvdW5jZXIgRGVib3VuY2VyIG9iamVjdC4KICAgICAqIEBwYXJhbSB7IUFzeW5jSW50ZXJmYWNlfSBhc3luY01vZHVsZSBPYmplY3Qgd2l0aCBBc3luYyBpbnRlcmZhY2UKICAgICAqIEBwYXJhbSB7ZnVuY3Rpb24oKX0gY2FsbGJhY2sgQ2FsbGJhY2sgdG8gcnVuLgogICAgICogQHJldHVybiB7IURlYm91bmNlcn0gUmV0dXJucyBhIGRlYm91bmNlciBvYmplY3QuCiAgICAgKi8KICAgIHN0YXRpYyBkZWJvdW5jZShkZWJvdW5jZXIsIGFzeW5jTW9kdWxlLCBjYWxsYmFjaykgewogICAgICBpZiAoZGVib3VuY2VyIGluc3RhbmNlb2YgRGVib3VuY2VyKSB7CiAgICAgICAgZGVib3VuY2VyLmNhbmNlbCgpOwogICAgICB9IGVsc2UgewogICAgICAgIGRlYm91bmNlciA9IG5ldyBEZWJvdW5jZXIoKTsKICAgICAgfQogICAgICBkZWJvdW5jZXIuc2V0Q29uZmlnKGFzeW5jTW9kdWxlLCBjYWxsYmFjayk7CiAgICAgIHJldHVybiBkZWJvdW5jZXI7CiAgICB9CiAgfQoKICAvKiogQGNvbnN0ICovCiAgUG9seW1lci5EZWJvdW5jZXIgPSBEZWJvdW5jZXI7Cn0pKCk7Cjwvc2NyaXB0PjxzY3JpcHQ+CihmdW5jdGlvbigpIHsKCiAgJ3VzZSBzdHJpY3QnOwoKICAvLyBkZXRlY3QgbmF0aXZlIHRvdWNoIGFjdGlvbiBzdXBwb3J0CiAgbGV0IEhBU19OQVRJVkVfVEEgPSB0eXBlb2YgZG9jdW1lbnQuaGVhZC5zdHlsZS50b3VjaEFjdGlvbiA9PT0gJ3N0cmluZyc7CiAgbGV0IEdFU1RVUkVfS0VZID0gJ19fcG9seW1lckdlc3R1cmVzJzsKICBsZXQgSEFORExFRF9PQkogPSAnX19wb2x5bWVyR2VzdHVyZXNIYW5kbGVkJzsKICBsZXQgVE9VQ0hfQUNUSU9OID0gJ19fcG9seW1lckdlc3R1cmVzVG91Y2hBY3Rpb24nOwogIC8vIHJhZGl1cyBmb3IgdGFwIGFuZCB0cmFjawogIGxldCBUQVBfRElTVEFOQ0UgPSAyNTsKICBsZXQgVFJBQ0tfRElTVEFOQ0UgPSA1OwogIC8vIG51bWJlciBvZiBsYXN0IE4gdHJhY2sgcG9zaXRpb25zIHRvIGtlZXAKICBsZXQgVFJBQ0tfTEVOR1RIID0gMjsKCiAgLy8gRGlzYWJsaW5nICJtb3VzZSIgaGFuZGxlcnMgZm9yIDI1MDBtcyBpcyBlbm91Z2gKICBsZXQgTU9VU0VfVElNRU9VVCA9IDI1MDA7CiAgbGV0IE1PVVNFX0VWRU5UUyA9IFsnbW91c2Vkb3duJywgJ21vdXNlbW92ZScsICdtb3VzZXVwJywgJ2NsaWNrJ107CiAgLy8gYW4gYXJyYXkgb2YgYml0bWFzayB2YWx1ZXMgZm9yIG1hcHBpbmcgTW91c2VFdmVudC53aGljaCB0byBNb3VzZUV2ZW50LmJ1dHRvbnMKICBsZXQgTU9VU0VfV0hJQ0hfVE9fQlVUVE9OUyA9IFswLCAxLCA0LCAyXTsKICBsZXQgTU9VU0VfSEFTX0JVVFRPTlMgPSAoZnVuY3Rpb24oKSB7CiAgICB0cnkgewogICAgICByZXR1cm4gbmV3IE1vdXNlRXZlbnQoJ3Rlc3QnLCB7YnV0dG9uczogMX0pLmJ1dHRvbnMgPT09IDE7CiAgICB9IGNhdGNoIChlKSB7CiAgICAgIHJldHVybiBmYWxzZTsKICAgIH0KICB9KSgpOwoKICAvKioKICAgKiBAcGFyYW0ge3N0cmluZ30gbmFtZSBQb3NzaWJsZSBtb3VzZSBldmVudCBuYW1lCiAgICogQHJldHVybiB7Ym9vbGVhbn0gdHJ1ZSBpZiBtb3VzZSBldmVudCwgZmFsc2UgaWYgbm90CiAgICovCiAgZnVuY3Rpb24gaXNNb3VzZUV2ZW50KG5hbWUpIHsKICAgIHJldHVybiBNT1VTRV9FVkVOVFMuaW5kZXhPZihuYW1lKSA+IC0xOwogIH0KCiAgLyogZXNsaW50IG5vLWVtcHR5OiBbImVycm9yIiwgeyAiYWxsb3dFbXB0eUNhdGNoIjogdHJ1ZSB9XSAqLwogIC8vIGNoZWNrIGZvciBwYXNzaXZlIGV2ZW50IGxpc3RlbmVycwogIGxldCBTVVBQT1JUU19QQVNTSVZFID0gZmFsc2U7CiAgKGZ1bmN0aW9uKCkgewogICAgdHJ5IHsKICAgICAgbGV0IG9wdHMgPSBPYmplY3QuZGVmaW5lUHJvcGVydHkoe30sICdwYXNzaXZlJywge2dldCgpIHtTVVBQT1JUU19QQVNTSVZFID0gdHJ1ZTt9fSk7CiAgICAgIHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCd0ZXN0JywgbnVsbCwgb3B0cyk7CiAgICAgIHdpbmRvdy5yZW1vdmVFdmVudExpc3RlbmVyKCd0ZXN0JywgbnVsbCwgb3B0cyk7CiAgICB9IGNhdGNoKGUpIHt9CiAgfSkoKTsKCiAgLyoqCiAgICogR2VuZXJhdGUgc2V0dGluZ3MgZm9yIGV2ZW50IGxpc3RlbmVycywgZGVwZW5kYW50IG9uIGBQb2x5bWVyLnBhc3NpdmVUb3VjaEdlc3R1cmVzYAogICAqCiAgICogQHBhcmFtIHtzdHJpbmd9IGV2ZW50TmFtZSBFdmVudCBuYW1lIHRvIGRldGVybWluZSBpZiBge3Bhc3NpdmV9YCBvcHRpb24gaXMgbmVlZGVkCiAgICogQHJldHVybiB7e3Bhc3NpdmU6IGJvb2xlYW59IHwgdW5kZWZpbmVkfSBPcHRpb25zIHRvIHVzZSBmb3IgYWRkRXZlbnRMaXN0ZW5lciBhbmQgcmVtb3ZlRXZlbnRMaXN0ZW5lcgogICAqLwogIGZ1bmN0aW9uIFBBU1NJVkVfVE9VQ0goZXZlbnROYW1lKSB7CiAgICBpZiAoaXNNb3VzZUV2ZW50KGV2ZW50TmFtZSkgfHwgZXZlbnROYW1lID09PSAndG91Y2hlbmQnKSB7CiAgICAgIHJldHVybjsKICAgIH0KICAgIGlmIChIQVNfTkFUSVZFX1RBICYmIFNVUFBPUlRTX1BBU1NJVkUgJiYgUG9seW1lci5wYXNzaXZlVG91Y2hHZXN0dXJlcykgewogICAgICByZXR1cm4ge3Bhc3NpdmU6IHRydWV9OwogICAgfSBlbHNlIHsKICAgICAgcmV0dXJuOwogICAgfQogIH0KCiAgLy8gQ2hlY2sgZm9yIHRvdWNoLW9ubHkgZGV2aWNlcwogIGxldCBJU19UT1VDSF9PTkxZID0gbmF2aWdhdG9yLnVzZXJBZ2VudC5tYXRjaCgvaVAoPzpbb2FdZHxob25lKXxBbmRyb2lkLyk7CgogIGxldCBHZXN0dXJlUmVjb2duaXplciA9IGZ1bmN0aW9uKCl7fTsgLy8gZXNsaW50LWRpc2FibGUtbGluZSBuby11bnVzZWQtdmFycwogIC8qKiBAdHlwZSB7ZnVuY3Rpb24oKTogdm9pZH0gKi8KICBHZXN0dXJlUmVjb2duaXplci5wcm90b3R5cGUucmVzZXQ7CiAgLyoqIEB0eXBlIHtmdW5jdGlvbihNb3VzZUV2ZW50KTogdm9pZCB8IHVuZGVmaW5lZH0gKi8KICBHZXN0dXJlUmVjb2duaXplci5wcm90b3R5cGUubW91c2Vkb3duOwogIC8qKiBAdHlwZSB7KGZ1bmN0aW9uKE1vdXNlRXZlbnQpOiB2b2lkIHwgdW5kZWZpbmVkKX0gKi8KICBHZXN0dXJlUmVjb2duaXplci5wcm90b3R5cGUubW91c2Vtb3ZlOwogIC8qKiBAdHlwZSB7KGZ1bmN0aW9uKE1vdXNlRXZlbnQpOiB2b2lkIHwgdW5kZWZpbmVkKX0gKi8KICBHZXN0dXJlUmVjb2duaXplci5wcm90b3R5cGUubW91c2V1cDsKICAvKiogQHR5cGUgeyhmdW5jdGlvbihUb3VjaEV2ZW50KTogdm9pZCB8IHVuZGVmaW5lZCl9ICovCiAgR2VzdHVyZVJlY29nbml6ZXIucHJvdG90eXBlLnRvdWNoc3RhcnQ7CiAgLyoqIEB0eXBlIHsoZnVuY3Rpb24oVG91Y2hFdmVudCk6IHZvaWQgfCB1bmRlZmluZWQpfSAqLwogIEdlc3R1cmVSZWNvZ25pemVyLnByb3RvdHlwZS50b3VjaG1vdmU7CiAgLyoqIEB0eXBlIHsoZnVuY3Rpb24oVG91Y2hFdmVudCk6IHZvaWQgfCB1bmRlZmluZWQpfSAqLwogIEdlc3R1cmVSZWNvZ25pemVyLnByb3RvdHlwZS50b3VjaGVuZDsKICAvKiogQHR5cGUgeyhmdW5jdGlvbihNb3VzZUV2ZW50KTogdm9pZCB8IHVuZGVmaW5lZCl9ICovCiAgR2VzdHVyZVJlY29nbml6ZXIucHJvdG90eXBlLmNsaWNrOwoKICAvLyBrZWVwIHRyYWNrIG9mIGFueSBsYWJlbHMgaGl0IGJ5IHRoZSBtb3VzZUNhbmNlbGxlcgogIC8qKiBAdHlwZSB7IUFycmF5PCFIVE1MTGFiZWxFbGVtZW50Pn0gKi8KICBjb25zdCBjbGlja2VkTGFiZWxzID0gW107CgogIC8qKiBAdHlwZSB7IU9iamVjdDxib29sZWFuPn0gKi8KICBjb25zdCBsYWJlbGxhYmxlID0gewogICAgJ2J1dHRvbic6IHRydWUsCiAgICAnaW5wdXQnOiB0cnVlLAogICAgJ2tleWdlbic6IHRydWUsCiAgICAnbWV0ZXInOiB0cnVlLAogICAgJ291dHB1dCc6IHRydWUsCiAgICAndGV4dGFyZWEnOiB0cnVlLAogICAgJ3Byb2dyZXNzJzogdHJ1ZSwKICAgICdzZWxlY3QnOiB0cnVlCiAgfTsKCiAgLy8gRGVmaW5lZCBhdCBodHRwczovL2h0bWwuc3BlYy53aGF0d2cub3JnL211bHRpcGFnZS9mb3JtLWNvbnRyb2wtaW5mcmFzdHJ1Y3R1cmUuaHRtbCNlbmFibGluZy1hbmQtZGlzYWJsaW5nLWZvcm0tY29udHJvbHM6LXRoZS1kaXNhYmxlZC1hdHRyaWJ1dGUKICAvKiogQHR5cGUgeyFPYmplY3Q8Ym9vbGVhbj59ICovCiAgY29uc3QgY2FuQmVEaXNhYmxlZCA9IHsKICAgICdidXR0b24nOiB0cnVlLAogICAgJ2NvbW1hbmQnOiB0cnVlLAogICAgJ2ZpZWxkc2V0JzogdHJ1ZSwKICAgICdpbnB1dCc6IHRydWUsCiAgICAna2V5Z2VuJzogdHJ1ZSwKICAgICdvcHRncm91cCc6IHRydWUsCiAgICAnb3B0aW9uJzogdHJ1ZSwKICAgICdzZWxlY3QnOiB0cnVlLAogICAgJ3RleHRhcmVhJzogdHJ1ZQogIH07CgogIC8qKgogICAqIEBwYXJhbSB7SFRNTEVsZW1lbnR9IGVsIEVsZW1lbnQgdG8gY2hlY2sgbGFiZWxsaW5nIHN0YXR1cwogICAqIEByZXR1cm4ge2Jvb2xlYW59IGVsZW1lbnQgY2FuIGhhdmUgbGFiZWxzCiAgICovCiAgZnVuY3Rpb24gY2FuQmVMYWJlbGxlZChlbCkgewogICAgcmV0dXJuIGxhYmVsbGFibGVbZWwubG9jYWxOYW1lXSB8fCBmYWxzZTsKICB9CgogIC8qKgogICAqIEBwYXJhbSB7SFRNTEVsZW1lbnR9IGVsIEVsZW1lbnQgdGhhdCBtYXkgYmUgbGFiZWxsZWQuCiAgICogQHJldHVybiB7IUFycmF5PCFIVE1MTGFiZWxFbGVtZW50Pn0gUmVsZXZhbnQgbGFiZWwgZm9yIGBlbGAKICAgKi8KICBmdW5jdGlvbiBtYXRjaGluZ0xhYmVscyhlbCkgewogICAgbGV0IGxhYmVscyA9IEFycmF5LmZyb20oLyoqIEB0eXBlIHtIVE1MSW5wdXRFbGVtZW50fSAqLyhlbCkubGFiZWxzIHx8IFtdKTsKICAgIC8vIElFIGRvZXNuJ3QgaGF2ZSBgbGFiZWxzYCBhbmQgU2FmYXJpIGRvZXNuJ3QgcG9wdWxhdGUgYGxhYmVsc2AKICAgIC8vIGlmIGVsZW1lbnQgaXMgaW4gYSBzaGFkb3dyb290LgogICAgLy8gSW4gdGhpcyBpbnN0YW5jZSwgZmluZGluZyB0aGUgbm9uLWFuY2VzdG9yIGxhYmVscyBpcyBlbm91Z2gsCiAgICAvLyBhcyB0aGUgbW91c2VDYW5jZWxsb3IgY29kZSB3aWxsIGhhbmRsZSBhbmNzdG9yIGxhYmVscwogICAgaWYgKCFsYWJlbHMubGVuZ3RoKSB7CiAgICAgIGxhYmVscyA9IFtdOwogICAgICBsZXQgcm9vdCA9IGVsLmdldFJvb3ROb2RlKCk7CiAgICAgIC8vIGlmIHRoZXJlIGlzIGFuIGlkIG9uIGBlbGAsIGNoZWNrIGZvciBhbGwgbGFiZWxzIHdpdGggYSBtYXRjaGluZyBgZm9yYCBhdHRyaWJ1dGUKICAgICAgaWYgKGVsLmlkKSB7CiAgICAgICAgbGV0IG1hdGNoaW5nID0gcm9vdC5xdWVyeVNlbGVjdG9yQWxsKGBsYWJlbFtmb3IgPSAke2VsLmlkfV1gKTsKICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IG1hdGNoaW5nLmxlbmd0aDsgaSsrKSB7CiAgICAgICAgICBsYWJlbHMucHVzaCgvKiogQHR5cGUgeyFIVE1MTGFiZWxFbGVtZW50fSAqLyhtYXRjaGluZ1tpXSkpOwogICAgICAgIH0KICAgICAgfQogICAgfQogICAgcmV0dXJuIGxhYmVsczsKICB9CgogIC8vIHRvdWNoIHdpbGwgbWFrZSBzeW50aGV0aWMgbW91c2UgZXZlbnRzCiAgLy8gYHByZXZlbnREZWZhdWx0YCBvbiB0b3VjaGVuZCB3aWxsIGNhbmNlbCB0aGVtLAogIC8vIGJ1dCB0aGlzIGJyZWFrcyBgPGlucHV0PmAgZm9jdXMgYW5kIGxpbmsgY2xpY2tzCiAgLy8gZGlzYWJsZSBtb3VzZSBoYW5kbGVycyBmb3IgTU9VU0VfVElNRU9VVCBtcyBhZnRlcgogIC8vIGEgdG91Y2hlbmQgdG8gaWdub3JlIHN5bnRoZXRpYyBtb3VzZSBldmVudHMKICBsZXQgbW91c2VDYW5jZWxsZXIgPSBmdW5jdGlvbihtb3VzZUV2ZW50KSB7CiAgICAvLyBDaGVjayBmb3Igc291cmNlQ2FwYWJpbGl0aWVzLCB1c2VkIHRvIGRpc3Rpbmd1aXNoIHN5bnRoZXRpYyBldmVudHMKICAgIC8vIGlmIG1vdXNlRXZlbnQgZGlkIG5vdCBjb21lIGZyb20gYSBkZXZpY2UgdGhhdCBmaXJlcyB0b3VjaCBldmVudHMsCiAgICAvLyBpdCB3YXMgbWFkZSBieSBhIHJlYWwgbW91c2UgYW5kIHNob3VsZCBiZSBjb3VudGVkCiAgICAvLyBodHRwOi8vd2ljZy5naXRodWIuaW8vSW5wdXREZXZpY2VDYXBhYmlsaXRpZXMvI2RvbS1pbnB1dGRldmljZWNhcGFiaWxpdGllcy1maXJlc3RvdWNoZXZlbnRzCiAgICBsZXQgc2MgPSBtb3VzZUV2ZW50LnNvdXJjZUNhcGFiaWxpdGllczsKICAgIGlmIChzYyAmJiAhc2MuZmlyZXNUb3VjaEV2ZW50cykgewogICAgICByZXR1cm47CiAgICB9CiAgICAvLyBza2lwIHN5bnRoZXRpYyBtb3VzZSBldmVudHMKICAgIG1vdXNlRXZlbnRbSEFORExFRF9PQkpdID0ge3NraXA6IHRydWV9OwogICAgLy8gZGlzYWJsZSAiZ2hvc3QgY2xpY2tzIgogICAgaWYgKG1vdXNlRXZlbnQudHlwZSA9PT0gJ2NsaWNrJykgewogICAgICBsZXQgY2xpY2tGcm9tTGFiZWwgPSBmYWxzZTsKICAgICAgbGV0IHBhdGggPSBtb3VzZUV2ZW50LmNvbXBvc2VkUGF0aCAmJiBtb3VzZUV2ZW50LmNvbXBvc2VkUGF0aCgpOwogICAgICBpZiAocGF0aCkgewogICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgcGF0aC5sZW5ndGg7IGkrKykgewogICAgICAgICAgaWYgKHBhdGhbaV0ubm9kZVR5cGUgPT09IE5vZGUuRUxFTUVOVF9OT0RFKSB7CiAgICAgICAgICAgIGlmIChwYXRoW2ldLmxvY2FsTmFtZSA9PT0gJ2xhYmVsJykgewogICAgICAgICAgICAgIGNsaWNrZWRMYWJlbHMucHVzaChwYXRoW2ldKTsKICAgICAgICAgICAgfSBlbHNlIGlmIChjYW5CZUxhYmVsbGVkKHBhdGhbaV0pKSB7CiAgICAgICAgICAgICAgbGV0IG93bmVyTGFiZWxzID0gbWF0Y2hpbmdMYWJlbHMocGF0aFtpXSk7CiAgICAgICAgICAgICAgLy8gY2hlY2sgaWYgb25lIG9mIHRoZSBjbGlja2VkIGxhYmVscyBpcyBsYWJlbGxpbmcgdGhpcyBlbGVtZW50CiAgICAgICAgICAgICAgZm9yIChsZXQgaiA9IDA7IGogPCBvd25lckxhYmVscy5sZW5ndGg7IGorKykgewogICAgICAgICAgICAgICAgY2xpY2tGcm9tTGFiZWwgPSBjbGlja0Zyb21MYWJlbCB8fCBjbGlja2VkTGFiZWxzLmluZGV4T2Yob3duZXJMYWJlbHNbal0pID4gLTE7CiAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CiAgICAgICAgICB9CiAgICAgICAgICBpZiAocGF0aFtpXSA9PT0gUE9JTlRFUlNUQVRFLm1vdXNlLnRhcmdldCkgewogICAgICAgICAgICByZXR1cm47CiAgICAgICAgICB9CiAgICAgICAgfQogICAgICB9CiAgICAgIC8vIGlmIG9uZSBvZiB0aGUgY2xpY2tlZCBsYWJlbHMgd2FzIGxhYmVsbGluZyB0aGUgdGFyZ2V0IGVsZW1lbnQsCiAgICAgIC8vIHRoaXMgaXMgbm90IGEgZ2hvc3QgY2xpY2sKICAgICAgaWYgKGNsaWNrRnJvbUxhYmVsKSB7CiAgICAgICAgcmV0dXJuOwogICAgICB9CiAgICAgIG1vdXNlRXZlbnQucHJldmVudERlZmF1bHQoKTsKICAgICAgbW91c2VFdmVudC5zdG9wUHJvcGFnYXRpb24oKTsKICAgIH0KICB9OwoKICAvKioKICAgKiBAcGFyYW0ge2Jvb2xlYW49fSBzZXR1cCBUcnVlIHRvIGFkZCwgZmFsc2UgdG8gcmVtb3ZlLgogICAqIEByZXR1cm4ge3ZvaWR9CiAgICovCiAgZnVuY3Rpb24gc2V0dXBUZWFyZG93bk1vdXNlQ2FuY2VsbGVyKHNldHVwKSB7CiAgICBsZXQgZXZlbnRzID0gSVNfVE9VQ0hfT05MWSA/IFsnY2xpY2snXSA6IE1PVVNFX0VWRU5UUzsKICAgIGZvciAobGV0IGkgPSAwLCBlbjsgaSA8IGV2ZW50cy5sZW5ndGg7IGkrKykgewogICAgICBlbiA9IGV2ZW50c1tpXTsKICAgICAgaWYgKHNldHVwKSB7CiAgICAgICAgLy8gcmVzZXQgY2xpY2tMYWJlbHMgYXJyYXkKICAgICAgICBjbGlja2VkTGFiZWxzLmxlbmd0aCA9IDA7CiAgICAgICAgZG9jdW1lbnQuYWRkRXZlbnRMaXN0ZW5lcihlbiwgbW91c2VDYW5jZWxsZXIsIHRydWUpOwogICAgICB9IGVsc2UgewogICAgICAgIGRvY3VtZW50LnJlbW92ZUV2ZW50TGlzdGVuZXIoZW4sIG1vdXNlQ2FuY2VsbGVyLCB0cnVlKTsKICAgICAgfQogICAgfQogIH0KCiAgZnVuY3Rpb24gaWdub3JlTW91c2UoZSkgewogICAgaWYgKCFQT0lOVEVSU1RBVEUubW91c2UubW91c2VJZ25vcmVKb2IpIHsKICAgICAgc2V0dXBUZWFyZG93bk1vdXNlQ2FuY2VsbGVyKHRydWUpOwogICAgfQogICAgbGV0IHVuc2V0ID0gZnVuY3Rpb24oKSB7CiAgICAgIHNldHVwVGVhcmRvd25Nb3VzZUNhbmNlbGxlcigpOwogICAgICBQT0lOVEVSU1RBVEUubW91c2UudGFyZ2V0ID0gbnVsbDsKICAgICAgUE9JTlRFUlNUQVRFLm1vdXNlLm1vdXNlSWdub3JlSm9iID0gbnVsbDsKICAgIH07CiAgICBQT0lOVEVSU1RBVEUubW91c2UudGFyZ2V0ID0gZS5jb21wb3NlZFBhdGgoKVswXTsKICAgIFBPSU5URVJTVEFURS5tb3VzZS5tb3VzZUlnbm9yZUpvYiA9IFBvbHltZXIuRGVib3VuY2VyLmRlYm91bmNlKAogICAgICAgICAgUE9JTlRFUlNUQVRFLm1vdXNlLm1vdXNlSWdub3JlSm9iCiAgICAgICAgLCBQb2x5bWVyLkFzeW5jLnRpbWVPdXQuYWZ0ZXIoTU9VU0VfVElNRU9VVCkKICAgICAgICAsIHVuc2V0KTsKICB9CgogIC8qKgogICAqIEBwYXJhbSB7TW91c2VFdmVudH0gZXYgZXZlbnQgdG8gdGVzdCBmb3IgbGVmdCBtb3VzZSBidXR0b24gZG93bgogICAqIEByZXR1cm4ge2Jvb2xlYW59IGhhcyBsZWZ0IG1vdXNlIGJ1dHRvbiBkb3duCiAgICovCiAgZnVuY3Rpb24gaGFzTGVmdE1vdXNlQnV0dG9uKGV2KSB7CiAgICBsZXQgdHlwZSA9IGV2LnR5cGU7CiAgICAvLyBleGl0IGVhcmx5IGlmIHRoZSBldmVudCBpcyBub3QgYSBtb3VzZSBldmVudAogICAgaWYgKCFpc01vdXNlRXZlbnQodHlwZSkpIHsKICAgICAgcmV0dXJuIGZhbHNlOwogICAgfQogICAgLy8gZXYuYnV0dG9uIGlzIG5vdCByZWxpYWJsZSBmb3IgbW91c2Vtb3ZlICgwIGlzIG92ZXJsb2FkZWQgYXMgYm90aCBsZWZ0IGJ1dHRvbiBhbmQgbm8gYnV0dG9ucykKICAgIC8vIGluc3RlYWQgd2UgdXNlIGV2LmJ1dHRvbnMgKGJpdG1hc2sgb2YgYnV0dG9ucykgb3IgZmFsbCBiYWNrIHRvIGV2LndoaWNoIChkZXByZWNhdGVkLCAwIGZvciBubyBidXR0b25zLCAxIGZvciBsZWZ0IGJ1dHRvbikKICAgIGlmICh0eXBlID09PSAnbW91c2Vtb3ZlJykgewogICAgICAvLyBhbGxvdyB1bmRlZmluZWQgZm9yIHRlc3RpbmcgZXZlbnRzCiAgICAgIGxldCBidXR0b25zID0gZXYuYnV0dG9ucyA9PT0gdW5kZWZpbmVkID8gMSA6IGV2LmJ1dHRvbnM7CiAgICAgIGlmICgoZXYgaW5zdGFuY2VvZiB3aW5kb3cuTW91c2VFdmVudCkgJiYgIU1PVVNFX0hBU19CVVRUT05TKSB7CiAgICAgICAgYnV0dG9ucyA9IE1PVVNFX1dISUNIX1RPX0JVVFRPTlNbZXYud2hpY2hdIHx8IDA7CiAgICAgIH0KICAgICAgLy8gYnV0dG9ucyBpcyBhIGJpdG1hc2ssIGNoZWNrIHRoYXQgdGhlIGxlZnQgYnV0dG9uIGJpdCBpcyBzZXQgKDEpCiAgICAgIHJldHVybiBCb29sZWFuKGJ1dHRvbnMgJiAxKTsKICAgIH0gZWxzZSB7CiAgICAgIC8vIGFsbG93IHVuZGVmaW5lZCBmb3IgdGVzdGluZyBldmVudHMKICAgICAgbGV0IGJ1dHRvbiA9IGV2LmJ1dHRvbiA9PT0gdW5kZWZpbmVkID8gMCA6IGV2LmJ1dHRvbjsKICAgICAgLy8gZXYuYnV0dG9uIGlzIDAgaW4gbW91c2Vkb3duL21vdXNldXAvY2xpY2sgZm9yIGxlZnQgYnV0dG9uIGFjdGl2YXRpb24KICAgICAgcmV0dXJuIGJ1dHRvbiA9PT0gMDsKICAgIH0KICB9CgogIGZ1bmN0aW9uIGlzU3ludGhldGljQ2xpY2soZXYpIHsKICAgIGlmIChldi50eXBlID09PSAnY2xpY2snKSB7CiAgICAgIC8vIGV2LmRldGFpbCBpcyAwIGZvciBIVE1MRWxlbWVudC5jbGljayBpbiBtb3N0IGJyb3dzZXJzCiAgICAgIGlmIChldi5kZXRhaWwgPT09IDApIHsKICAgICAgICByZXR1cm4gdHJ1ZTsKICAgICAgfQogICAgICAvLyBpbiB0aGUgd29yc3QgY2FzZSwgY2hlY2sgdGhhdCB0aGUgeC95IHBvc2l0aW9uIG9mIHRoZSBjbGljayBpcyB3aXRoaW4KICAgICAgLy8gdGhlIGJvdW5kaW5nIGJveCBvZiB0aGUgdGFyZ2V0IG9mIHRoZSBldmVudAogICAgICAvLyBUaGFua3MgSUUgMTAgPjooCiAgICAgIGxldCB0ID0gR2VzdHVyZXMuX2ZpbmRPcmlnaW5hbFRhcmdldChldik7CiAgICAgIC8vIG1ha2Ugc3VyZSB0aGUgdGFyZ2V0IG9mIHRoZSBldmVudCBpcyBhbiBlbGVtZW50IHNvIHdlIGNhbiB1c2UgZ2V0Qm91bmRpbmdDbGllbnRSZWN0LAogICAgICAvLyBpZiBub3QsIGp1c3QgYXNzdW1lIGl0IGlzIGEgc3ludGhldGljIGNsaWNrCiAgICAgIGlmICghdC5ub2RlVHlwZSB8fCAvKiogQHR5cGUge0VsZW1lbnR9ICovKHQpLm5vZGVUeXBlICE9PSBOb2RlLkVMRU1FTlRfTk9ERSkgewogICAgICAgIHJldHVybiB0cnVlOwogICAgICB9CiAgICAgIGxldCBiY3IgPSAvKiogQHR5cGUge0VsZW1lbnR9ICovKHQpLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpOwogICAgICAvLyB1c2UgcGFnZSB4L3kgdG8gYWNjb3VudCBmb3Igc2Nyb2xsaW5nCiAgICAgIGxldCB4ID0gZXYucGFnZVgsIHkgPSBldi5wYWdlWTsKICAgICAgLy8gZXYgaXMgYSBzeW50aGV0aWMgY2xpY2sgaWYgdGhlIHBvc2l0aW9uIGlzIG91dHNpZGUgdGhlIGJvdW5kaW5nIGJveCBvZiB0aGUgdGFyZ2V0CiAgICAgIHJldHVybiAhKCh4ID49IGJjci5sZWZ0ICYmIHggPD0gYmNyLnJpZ2h0KSAmJiAoeSA+PSBiY3IudG9wICYmIHkgPD0gYmNyLmJvdHRvbSkpOwogICAgfQogICAgcmV0dXJuIGZhbHNlOwogIH0KCiAgbGV0IFBPSU5URVJTVEFURSA9IHsKICAgIG1vdXNlOiB7CiAgICAgIHRhcmdldDogbnVsbCwKICAgICAgbW91c2VJZ25vcmVKb2I6IG51bGwKICAgIH0sCiAgICB0b3VjaDogewogICAgICB4OiAwLAogICAgICB5OiAwLAogICAgICBpZDogLTEsCiAgICAgIHNjcm9sbERlY2lkZWQ6IGZhbHNlCiAgICB9CiAgfTsKCiAgZnVuY3Rpb24gZmlyc3RUb3VjaEFjdGlvbihldikgewogICAgbGV0IHRhID0gJ2F1dG8nOwogICAgbGV0IHBhdGggPSBldi5jb21wb3NlZFBhdGggJiYgZXYuY29tcG9zZWRQYXRoKCk7CiAgICBpZiAocGF0aCkgewogICAgICBmb3IgKGxldCBpID0gMCwgbjsgaSA8IHBhdGgubGVuZ3RoOyBpKyspIHsKICAgICAgICBuID0gcGF0aFtpXTsKICAgICAgICBpZiAobltUT1VDSF9BQ1RJT05dKSB7CiAgICAgICAgICB0YSA9IG5bVE9VQ0hfQUNUSU9OXTsKICAgICAgICAgIGJyZWFrOwogICAgICAgIH0KICAgICAgfQogICAgfQogICAgcmV0dXJuIHRhOwogIH0KCiAgZnVuY3Rpb24gdHJhY2tEb2N1bWVudChzdGF0ZU9iaiwgbW92ZWZuLCB1cGZuKSB7CiAgICBzdGF0ZU9iai5tb3ZlZm4gPSBtb3ZlZm47CiAgICBzdGF0ZU9iai51cGZuID0gdXBmbjsKICAgIGRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ21vdXNlbW92ZScsIG1vdmVmbik7CiAgICBkb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdtb3VzZXVwJywgdXBmbik7CiAgfQoKICBmdW5jdGlvbiB1bnRyYWNrRG9jdW1lbnQoc3RhdGVPYmopIHsKICAgIGRvY3VtZW50LnJlbW92ZUV2ZW50TGlzdGVuZXIoJ21vdXNlbW92ZScsIHN0YXRlT2JqLm1vdmVmbik7CiAgICBkb2N1bWVudC5yZW1vdmVFdmVudExpc3RlbmVyKCdtb3VzZXVwJywgc3RhdGVPYmoudXBmbik7CiAgICBzdGF0ZU9iai5tb3ZlZm4gPSBudWxsOwogICAgc3RhdGVPYmoudXBmbiA9IG51bGw7CiAgfQoKICAvLyB1c2UgYSBkb2N1bWVudC13aWRlIHRvdWNoZW5kIGxpc3RlbmVyIHRvIHN0YXJ0IHRoZSBnaG9zdC1jbGljayBwcmV2ZW50aW9uIG1lY2hhbmlzbQogIC8vIFVzZSBwYXNzaXZlIGV2ZW50IGxpc3RlbmVycywgaWYgc3VwcG9ydGVkLCB0byBub3QgYWZmZWN0IHNjcm9sbGluZyBwZXJmb3JtYW5jZQogIGRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ3RvdWNoZW5kJywgaWdub3JlTW91c2UsIFNVUFBPUlRTX1BBU1NJVkUgPyB7cGFzc2l2ZTogdHJ1ZX0gOiBmYWxzZSk7CgogIC8qKgogICAqIE1vZHVsZSBmb3IgYWRkaW5nIGxpc3RlbmVycyB0byBhIG5vZGUgZm9yIHRoZSBmb2xsb3dpbmcgbm9ybWFsaXplZAogICAqIGNyb3NzLXBsYXRmb3JtICJnZXN0dXJlIiBldmVudHM6CiAgICogLSBgZG93bmAgLSBtb3VzZSBvciB0b3VjaCB3ZW50IGRvd24KICAgKiAtIGB1cGAgLSBtb3VzZSBvciB0b3VjaCB3ZW50IHVwCiAgICogLSBgdGFwYCAtIG1vdXNlIGNsaWNrIG9yIGZpbmdlciB0YXAKICAgKiAtIGB0cmFja2AgLSBtb3VzZSBkcmFnIG9yIHRvdWNoIG1vdmUKICAgKgogICAqIEBuYW1lc3BhY2UKICAgKiBAbWVtYmVyb2YgUG9seW1lcgogICAqIEBzdW1tYXJ5IE1vZHVsZSBmb3IgYWRkaW5nIGNyb3NzLXBsYXRmb3JtIGdlc3R1cmUgZXZlbnQgbGlzdGVuZXJzLgogICAqLwogIGNvbnN0IEdlc3R1cmVzID0gewogICAgZ2VzdHVyZXM6IHt9LAogICAgcmVjb2duaXplcnM6IFtdLAoKICAgIC8qKgogICAgICogRmluZHMgdGhlIGVsZW1lbnQgcmVuZGVyZWQgb24gdGhlIHNjcmVlbiBhdCB0aGUgcHJvdmlkZWQgY29vcmRpbmF0ZXMuCiAgICAgKgogICAgICogU2ltaWxhciB0byBgZG9jdW1lbnQuZWxlbWVudEZyb21Qb2ludGAsIGJ1dCBwaWVyY2VzIHRocm91Z2gKICAgICAqIHNoYWRvdyByb290cy4KICAgICAqCiAgICAgKiBAbWVtYmVyb2YgUG9seW1lci5HZXN0dXJlcwogICAgICogQHBhcmFtIHtudW1iZXJ9IHggSG9yaXpvbnRhbCBwaXhlbCBjb29yZGluYXRlCiAgICAgKiBAcGFyYW0ge251bWJlcn0geSBWZXJ0aWNhbCBwaXhlbCBjb29yZGluYXRlCiAgICAgKiBAcmV0dXJuIHtFbGVtZW50fSBSZXR1cm5zIHRoZSBkZWVwZXN0IHNoYWRvd1Jvb3QgaW5jbHVzaXZlIGVsZW1lbnQKICAgICAqIGZvdW5kIGF0IHRoZSBzY3JlZW4gcG9zaXRpb24gZ2l2ZW4uCiAgICAgKi8KICAgIGRlZXBUYXJnZXRGaW5kOiBmdW5jdGlvbih4LCB5KSB7CiAgICAgIGxldCBub2RlID0gZG9jdW1lbnQuZWxlbWVudEZyb21Qb2ludCh4LCB5KTsKICAgICAgbGV0IG5leHQgPSBub2RlOwogICAgICAvLyB0aGlzIGNvZGUgcGF0aCBpcyBvbmx5IHRha2VuIHdoZW4gbmF0aXZlIFNoYWRvd0RPTSBpcyB1c2VkCiAgICAgIC8vIGlmIHRoZXJlIGlzIGEgc2hhZG93cm9vdCwgaXQgbWF5IGhhdmUgYSBub2RlIGF0IHgveQogICAgICAvLyBpZiB0aGVyZSBpcyBub3QgYSBzaGFkb3dyb290LCBleGl0IHRoZSBsb29wCiAgICAgIHdoaWxlIChuZXh0ICYmIG5leHQuc2hhZG93Um9vdCAmJiAhd2luZG93LlNoYWR5RE9NKSB7CiAgICAgICAgLy8gaWYgdGhlcmUgaXMgYSBub2RlIGF0IHgveSBpbiB0aGUgc2hhZG93cm9vdCwgbG9vayBkZWVwZXIKICAgICAgICBsZXQgb2xkTmV4dCA9IG5leHQ7CiAgICAgICAgbmV4dCA9IG5leHQuc2hhZG93Um9vdC5lbGVtZW50RnJvbVBvaW50KHgsIHkpOwogICAgICAgIC8vIG9uIFNhZmFyaSwgZWxlbWVudEZyb21Qb2ludCBtYXkgcmV0dXJuIHRoZSBzaGFkb3dSb290IGhvc3QKICAgICAgICBpZiAob2xkTmV4dCA9PT0gbmV4dCkgewogICAgICAgICAgYnJlYWs7CiAgICAgICAgfQogICAgICAgIGlmIChuZXh0KSB7CiAgICAgICAgICBub2RlID0gbmV4dDsKICAgICAgICB9CiAgICAgIH0KICAgICAgcmV0dXJuIG5vZGU7CiAgICB9LAogICAgLyoqCiAgICAgKiBhIGNoZWFwZXIgY2hlY2sgdGhhbiBldi5jb21wb3NlZFBhdGgoKVswXTsKICAgICAqCiAgICAgKiBAcHJpdmF0ZQogICAgICogQHBhcmFtIHtFdmVudH0gZXYgRXZlbnQuCiAgICAgKiBAcmV0dXJuIHtFdmVudFRhcmdldH0gUmV0dXJucyB0aGUgZXZlbnQgdGFyZ2V0LgogICAgICovCiAgICBfZmluZE9yaWdpbmFsVGFyZ2V0OiBmdW5jdGlvbihldikgewogICAgICAvLyBzaGFkb3dkb20KICAgICAgaWYgKGV2LmNvbXBvc2VkUGF0aCkgewogICAgICAgIGNvbnN0IHRhcmdldHMgPSAvKiogQHR5cGUgeyFBcnJheTwhRXZlbnRUYXJnZXQ+fSAqLyhldi5jb21wb3NlZFBhdGgoKSk7CiAgICAgICAgLy8gSXQgc2hvdWxkbid0IGJlLCBidXQgc29tZXRpbWVzIHRhcmdldHMgaXMgZW1wdHkgKHdpbmRvdyBvbiBTYWZhcmkpLgogICAgICAgIHJldHVybiB0YXJnZXRzLmxlbmd0aCA+IDAgPyB0YXJnZXRzWzBdIDogZXYudGFyZ2V0OwogICAgICB9CiAgICAgIC8vIHNoYWR5ZG9tCiAgICAgIHJldHVybiBldi50YXJnZXQ7CiAgICB9LAoKICAgIC8qKgogICAgICogQHByaXZhdGUKICAgICAqIEBwYXJhbSB7RXZlbnR9IGV2IEV2ZW50LgogICAgICogQHJldHVybiB7dm9pZH0KICAgICAqLwogICAgX2hhbmRsZU5hdGl2ZTogZnVuY3Rpb24oZXYpIHsKICAgICAgbGV0IGhhbmRsZWQ7CiAgICAgIGxldCB0eXBlID0gZXYudHlwZTsKICAgICAgbGV0IG5vZGUgPSBldi5jdXJyZW50VGFyZ2V0OwogICAgICBsZXQgZ29iaiA9IG5vZGVbR0VTVFVSRV9LRVldOwogICAgICBpZiAoIWdvYmopIHsKICAgICAgICByZXR1cm47CiAgICAgIH0KICAgICAgbGV0IGdzID0gZ29ialt0eXBlXTsKICAgICAgaWYgKCFncykgewogICAgICAgIHJldHVybjsKICAgICAgfQogICAgICBpZiAoIWV2W0hBTkRMRURfT0JKXSkgewogICAgICAgIGV2W0hBTkRMRURfT0JKXSA9IHt9OwogICAgICAgIGlmICh0eXBlLnNsaWNlKDAsIDUpID09PSAndG91Y2gnKSB7CiAgICAgICAgICBldiA9IC8qKiBAdHlwZSB7VG91Y2hFdmVudH0gKi8oZXYpOyAvLyBlc2xpbnQtZGlzYWJsZS1saW5lIG5vLXNlbGYtYXNzaWduCiAgICAgICAgICBsZXQgdCA9IGV2LmNoYW5nZWRUb3VjaGVzWzBdOwogICAgICAgICAgaWYgKHR5cGUgPT09ICd0b3VjaHN0YXJ0JykgewogICAgICAgICAgICAvLyBvbmx5IGhhbmRsZSB0aGUgZmlyc3QgZmluZ2VyCiAgICAgICAgICAgIGlmIChldi50b3VjaGVzLmxlbmd0aCA9PT0gMSkgewogICAgICAgICAgICAgIFBPSU5URVJTVEFURS50b3VjaC5pZCA9IHQuaWRlbnRpZmllcjsKICAgICAgICAgICAgfQogICAgICAgICAgfQogICAgICAgICAgaWYgKFBPSU5URVJTVEFURS50b3VjaC5pZCAhPT0gdC5pZGVudGlmaWVyKSB7CiAgICAgICAgICAgIHJldHVybjsKICAgICAgICAgIH0KICAgICAgICAgIGlmICghSEFTX05BVElWRV9UQSkgewogICAgICAgICAgICBpZiAodHlwZSA9PT0gJ3RvdWNoc3RhcnQnIHx8IHR5cGUgPT09ICd0b3VjaG1vdmUnKSB7CiAgICAgICAgICAgICAgR2VzdHVyZXMuX2hhbmRsZVRvdWNoQWN0aW9uKGV2KTsKICAgICAgICAgICAgfQogICAgICAgICAgfQogICAgICAgIH0KICAgICAgfQogICAgICBoYW5kbGVkID0gZXZbSEFORExFRF9PQkpdOwogICAgICAvLyB1c2VkIHRvIGlnbm9yZSBzeW50aGV0aWMgbW91c2UgZXZlbnRzCiAgICAgIGlmIChoYW5kbGVkLnNraXApIHsKICAgICAgICByZXR1cm47CiAgICAgIH0KICAgICAgLy8gcmVzZXQgcmVjb2duaXplciBzdGF0ZQogICAgICBmb3IgKGxldCBpID0gMCwgcjsgaSA8IEdlc3R1cmVzLnJlY29nbml6ZXJzLmxlbmd0aDsgaSsrKSB7CiAgICAgICAgciA9IEdlc3R1cmVzLnJlY29nbml6ZXJzW2ldOwogICAgICAgIGlmIChnc1tyLm5hbWVdICYmICFoYW5kbGVkW3IubmFtZV0pIHsKICAgICAgICAgIGlmIChyLmZsb3cgJiYgci5mbG93LnN0YXJ0LmluZGV4T2YoZXYudHlwZSkgPiAtMSAmJiByLnJlc2V0KSB7CiAgICAgICAgICAgIHIucmVzZXQoKTsKICAgICAgICAgIH0KICAgICAgICB9CiAgICAgIH0KICAgICAgLy8gZW5mb3JjZSBnZXN0dXJlIHJlY29nbml6ZXIgb3JkZXIKICAgICAgZm9yIChsZXQgaSA9IDAsIHI7IGkgPCBHZXN0dXJlcy5yZWNvZ25pemVycy5sZW5ndGg7IGkrKykgewogICAgICAgIHIgPSBHZXN0dXJlcy5yZWNvZ25pemVyc1tpXTsKICAgICAgICBpZiAoZ3Nbci5uYW1lXSAmJiAhaGFuZGxlZFtyLm5hbWVdKSB7CiAgICAgICAgICBoYW5kbGVkW3IubmFtZV0gPSB0cnVlOwogICAgICAgICAgclt0eXBlXShldik7CiAgICAgICAgfQogICAgICB9CiAgICB9LAoKICAgIC8qKgogICAgICogQHByaXZhdGUKICAgICAqIEBwYXJhbSB7VG91Y2hFdmVudH0gZXYgRXZlbnQuCiAgICAgKiBAcmV0dXJuIHt2b2lkfQogICAgICovCiAgICBfaGFuZGxlVG91Y2hBY3Rpb246IGZ1bmN0aW9uKGV2KSB7CiAgICAgIGxldCB0ID0gZXYuY2hhbmdlZFRvdWNoZXNbMF07CiAgICAgIGxldCB0eXBlID0gZXYudHlwZTsKICAgICAgaWYgKHR5cGUgPT09ICd0b3VjaHN0YXJ0JykgewogICAgICAgIFBPSU5URVJTVEFURS50b3VjaC54ID0gdC5jbGllbnRYOwogICAgICAgIFBPSU5URVJTVEFURS50b3VjaC55ID0gdC5jbGllbnRZOwogICAgICAgIFBPSU5URVJTVEFURS50b3VjaC5zY3JvbGxEZWNpZGVkID0gZmFsc2U7CiAgICAgIH0gZWxzZSBpZiAodHlwZSA9PT0gJ3RvdWNobW92ZScpIHsKICAgICAgICBpZiAoUE9JTlRFUlNUQVRFLnRvdWNoLnNjcm9sbERlY2lkZWQpIHsKICAgICAgICAgIHJldHVybjsKICAgICAgICB9CiAgICAgICAgUE9JTlRFUlNUQVRFLnRvdWNoLnNjcm9sbERlY2lkZWQgPSB0cnVlOwogICAgICAgIGxldCB0YSA9IGZpcnN0VG91Y2hBY3Rpb24oZXYpOwogICAgICAgIGxldCBwcmV2ZW50ID0gZmFsc2U7CiAgICAgICAgbGV0IGR4ID0gTWF0aC5hYnMoUE9JTlRFUlNUQVRFLnRvdWNoLnggLSB0LmNsaWVudFgpOwogICAgICAgIGxldCBkeSA9IE1hdGguYWJzKFBPSU5URVJTVEFURS50b3VjaC55IC0gdC5jbGllbnRZKTsKICAgICAgICBpZiAoIWV2LmNhbmNlbGFibGUpIHsKICAgICAgICAgIC8vIHNjcm9sbGluZyBpcyBoYXBwZW5pbmcKICAgICAgICB9IGVsc2UgaWYgKHRhID09PSAnbm9uZScpIHsKICAgICAgICAgIHByZXZlbnQgPSB0cnVlOwogICAgICAgIH0gZWxzZSBpZiAodGEgPT09ICdwYW4teCcpIHsKICAgICAgICAgIHByZXZlbnQgPSBkeSA+IGR4OwogICAgICAgIH0gZWxzZSBpZiAodGEgPT09ICdwYW4teScpIHsKICAgICAgICAgIHByZXZlbnQgPSBkeCA+IGR5OwogICAgICAgIH0KICAgICAgICBpZiAocHJldmVudCkgewogICAgICAgICAgZXYucHJldmVudERlZmF1bHQoKTsKICAgICAgICB9IGVsc2UgewogICAgICAgICAgR2VzdHVyZXMucHJldmVudCgndHJhY2snKTsKICAgICAgICB9CiAgICAgIH0KICAgIH0sCgogICAgLyoqCiAgICAgKiBBZGRzIGFuIGV2ZW50IGxpc3RlbmVyIHRvIGEgbm9kZSBmb3IgdGhlIGdpdmVuIGdlc3R1cmUgdHlwZS4KICAgICAqCiAgICAgKiBAbWVtYmVyb2YgUG9seW1lci5HZXN0dXJlcwogICAgICogQHBhcmFtIHshTm9kZX0gbm9kZSBOb2RlIHRvIGFkZCBsaXN0ZW5lciBvbgogICAgICogQHBhcmFtIHtzdHJpbmd9IGV2VHlwZSBHZXN0dXJlIHR5cGU6IGBkb3duYCwgYHVwYCwgYHRyYWNrYCwgb3IgYHRhcGAKICAgICAqIEBwYXJhbSB7IWZ1bmN0aW9uKCFFdmVudCk6dm9pZH0gaGFuZGxlciBFdmVudCBsaXN0ZW5lciBmdW5jdGlvbiB0byBjYWxsCiAgICAgKiBAcmV0dXJuIHtib29sZWFufSBSZXR1cm5zIHRydWUgaWYgYSBnZXN0dXJlIGV2ZW50IGxpc3RlbmVyIHdhcyBhZGRlZC4KICAgICAqIEB0aGlzIHtHZXN0dXJlc30KICAgICAqLwogICAgYWRkTGlzdGVuZXI6IGZ1bmN0aW9uKG5vZGUsIGV2VHlwZSwgaGFuZGxlcikgewogICAgICBpZiAodGhpcy5nZXN0dXJlc1tldlR5cGVdKSB7CiAgICAgICAgdGhpcy5fYWRkKG5vZGUsIGV2VHlwZSwgaGFuZGxlcik7CiAgICAgICAgcmV0dXJuIHRydWU7CiAgICAgIH0KICAgICAgcmV0dXJuIGZhbHNlOwogICAgfSwKCiAgICAvKioKICAgICAqIFJlbW92ZXMgYW4gZXZlbnQgbGlzdGVuZXIgZnJvbSBhIG5vZGUgZm9yIHRoZSBnaXZlbiBnZXN0dXJlIHR5cGUuCiAgICAgKgogICAgICogQG1lbWJlcm9mIFBvbHltZXIuR2VzdHVyZXMKICAgICAqIEBwYXJhbSB7IU5vZGV9IG5vZGUgTm9kZSB0byByZW1vdmUgbGlzdGVuZXIgZnJvbQogICAgICogQHBhcmFtIHtzdHJpbmd9IGV2VHlwZSBHZXN0dXJlIHR5cGU6IGBkb3duYCwgYHVwYCwgYHRyYWNrYCwgb3IgYHRhcGAKICAgICAqIEBwYXJhbSB7IWZ1bmN0aW9uKCFFdmVudCk6dm9pZH0gaGFuZGxlciBFdmVudCBsaXN0ZW5lciBmdW5jdGlvbiBwcmV2aW91c2x5IHBhc3NlZCB0bwogICAgICogIGBhZGRMaXN0ZW5lcmAuCiAgICAgKiBAcmV0dXJuIHtib29sZWFufSBSZXR1cm5zIHRydWUgaWYgYSBnZXN0dXJlIGV2ZW50IGxpc3RlbmVyIHdhcyByZW1vdmVkLgogICAgICogQHRoaXMge0dlc3R1cmVzfQogICAgICovCiAgICByZW1vdmVMaXN0ZW5lcjogZnVuY3Rpb24obm9kZSwgZXZUeXBlLCBoYW5kbGVyKSB7CiAgICAgIGlmICh0aGlzLmdlc3R1cmVzW2V2VHlwZV0pIHsKICAgICAgICB0aGlzLl9yZW1vdmUobm9kZSwgZXZUeXBlLCBoYW5kbGVyKTsKICAgICAgICByZXR1cm4gdHJ1ZTsKICAgICAgfQogICAgICByZXR1cm4gZmFsc2U7CiAgICB9LAoKICAgIC8qKgogICAgICogYXV0b21hdGUgdGhlIGV2ZW50IGxpc3RlbmVycyBmb3IgdGhlIG5hdGl2ZSBldmVudHMKICAgICAqCiAgICAgKiBAcHJpdmF0ZQogICAgICogQHBhcmFtIHshSFRNTEVsZW1lbnR9IG5vZGUgTm9kZSBvbiB3aGljaCB0byBhZGQgdGhlIGV2ZW50LgogICAgICogQHBhcmFtIHtzdHJpbmd9IGV2VHlwZSBFdmVudCB0eXBlIHRvIGFkZC4KICAgICAqIEBwYXJhbSB7ZnVuY3Rpb24oIUV2ZW50KX0gaGFuZGxlciBFdmVudCBoYW5kbGVyIGZ1bmN0aW9uLgogICAgICogQHJldHVybiB7dm9pZH0KICAgICAqIEB0aGlzIHtHZXN0dXJlc30KICAgICAqLwogICAgX2FkZDogZnVuY3Rpb24obm9kZSwgZXZUeXBlLCBoYW5kbGVyKSB7CiAgICAgIGxldCByZWNvZ25pemVyID0gdGhpcy5nZXN0dXJlc1tldlR5cGVdOwogICAgICBsZXQgZGVwcyA9IHJlY29nbml6ZXIuZGVwczsKICAgICAgbGV0IG5hbWUgPSByZWNvZ25pemVyLm5hbWU7CiAgICAgIGxldCBnb2JqID0gbm9kZVtHRVNUVVJFX0tFWV07CiAgICAgIGlmICghZ29iaikgewogICAgICAgIG5vZGVbR0VTVFVSRV9LRVldID0gZ29iaiA9IHt9OwogICAgICB9CiAgICAgIGZvciAobGV0IGkgPSAwLCBkZXAsIGdkOyBpIDwgZGVwcy5sZW5ndGg7IGkrKykgewogICAgICAgIGRlcCA9IGRlcHNbaV07CiAgICAgICAgLy8gZG9uJ3QgYWRkIG1vdXNlIGhhbmRsZXJzIG9uIGlPUyBiZWNhdXNlIHRoZXkgY2F1c2UgZ3JheSBzZWxlY3Rpb24gb3ZlcmxheXMKICAgICAgICBpZiAoSVNfVE9VQ0hfT05MWSAmJiBpc01vdXNlRXZlbnQoZGVwKSAmJiBkZXAgIT09ICdjbGljaycpIHsKICAgICAgICAgIGNvbnRpbnVlOwogICAgICAgIH0KICAgICAgICBnZCA9IGdvYmpbZGVwXTsKICAgICAgICBpZiAoIWdkKSB7CiAgICAgICAgICBnb2JqW2RlcF0gPSBnZCA9IHtfY291bnQ6IDB9OwogICAgICAgIH0KICAgICAgICBpZiAoZ2QuX2NvdW50ID09PSAwKSB7CiAgICAgICAgICBub2RlLmFkZEV2ZW50TGlzdGVuZXIoZGVwLCB0aGlzLl9oYW5kbGVOYXRpdmUsIFBBU1NJVkVfVE9VQ0goZGVwKSk7CiAgICAgICAgfQogICAgICAgIGdkW25hbWVdID0gKGdkW25hbWVdIHx8IDApICsgMTsKICAgICAgICBnZC5fY291bnQgPSAoZ2QuX2NvdW50IHx8IDApICsgMTsKICAgICAgfQogICAgICBub2RlLmFkZEV2ZW50TGlzdGVuZXIoZXZUeXBlLCBoYW5kbGVyKTsKICAgICAgaWYgKHJlY29nbml6ZXIudG91Y2hBY3Rpb24pIHsKICAgICAgICB0aGlzLnNldFRvdWNoQWN0aW9uKG5vZGUsIHJlY29nbml6ZXIudG91Y2hBY3Rpb24pOwogICAgICB9CiAgICB9LAoKICAgIC8qKgogICAgICogYXV0b21hdGUgZXZlbnQgbGlzdGVuZXIgcmVtb3ZhbCBmb3IgbmF0aXZlIGV2ZW50cwogICAgICoKICAgICAqIEBwcml2YXRlCiAgICAgKiBAcGFyYW0geyFIVE1MRWxlbWVudH0gbm9kZSBOb2RlIG9uIHdoaWNoIHRvIHJlbW92ZSB0aGUgZXZlbnQuCiAgICAgKiBAcGFyYW0ge3N0cmluZ30gZXZUeXBlIEV2ZW50IHR5cGUgdG8gcmVtb3ZlLgogICAgICogQHBhcmFtIHtmdW5jdGlvbihFdmVudD8pfSBoYW5kbGVyIEV2ZW50IGhhbmRsZXIgZnVuY3Rpb24uCiAgICAgKiBAcmV0dXJuIHt2b2lkfQogICAgICogQHRoaXMge0dlc3R1cmVzfQogICAgICovCiAgICBfcmVtb3ZlOiBmdW5jdGlvbihub2RlLCBldlR5cGUsIGhhbmRsZXIpIHsKICAgICAgbGV0IHJlY29nbml6ZXIgPSB0aGlzLmdlc3R1cmVzW2V2VHlwZV07CiAgICAgIGxldCBkZXBzID0gcmVjb2duaXplci5kZXBzOwogICAgICBsZXQgbmFtZSA9IHJlY29nbml6ZXIubmFtZTsKICAgICAgbGV0IGdvYmogPSBub2RlW0dFU1RVUkVfS0VZXTsKICAgICAgaWYgKGdvYmopIHsKICAgICAgICBmb3IgKGxldCBpID0gMCwgZGVwLCBnZDsgaSA8IGRlcHMubGVuZ3RoOyBpKyspIHsKICAgICAgICAgIGRlcCA9IGRlcHNbaV07CiAgICAgICAgICBnZCA9IGdvYmpbZGVwXTsKICAgICAgICAgIGlmIChnZCAmJiBnZFtuYW1lXSkgewogICAgICAgICAgICBnZFtuYW1lXSA9IChnZFtuYW1lXSB8fCAxKSAtIDE7CiAgICAgICAgICAgIGdkLl9jb3VudCA9IChnZC5fY291bnQgfHwgMSkgLSAxOwogICAgICAgICAgICBpZiAoZ2QuX2NvdW50ID09PSAwKSB7CiAgICAgICAgICAgICAgbm9kZS5yZW1vdmVFdmVudExpc3RlbmVyKGRlcCwgdGhpcy5faGFuZGxlTmF0aXZlLCBQQVNTSVZFX1RPVUNIKGRlcCkpOwogICAgICAgICAgICB9CiAgICAgICAgICB9CiAgICAgICAgfQogICAgICB9CiAgICAgIG5vZGUucmVtb3ZlRXZlbnRMaXN0ZW5lcihldlR5cGUsIGhhbmRsZXIpOwogICAgfSwKCiAgICAvKioKICAgICAqIFJlZ2lzdGVycyBhIG5ldyBnZXN0dXJlIGV2ZW50IHJlY29nbml6ZXIgZm9yIGFkZGluZyBuZXcgY3VzdG9tCiAgICAgKiBnZXN0dXJlIGV2ZW50IHR5cGVzLgogICAgICoKICAgICAqIEBtZW1iZXJvZiBQb2x5bWVyLkdlc3R1cmVzCiAgICAgKiBAcGFyYW0geyFHZXN0dXJlUmVjb2duaXplcn0gcmVjb2cgR2VzdHVyZSByZWNvZ25pemVyIGRlc2NyaXB0b3IKICAgICAqIEByZXR1cm4ge3ZvaWR9CiAgICAgKiBAdGhpcyB7R2VzdHVyZXN9CiAgICAgKi8KICAgIHJlZ2lzdGVyOiBmdW5jdGlvbihyZWNvZykgewogICAgICB0aGlzLnJlY29nbml6ZXJzLnB1c2gocmVjb2cpOwogICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHJlY29nLmVtaXRzLmxlbmd0aDsgaSsrKSB7CiAgICAgICAgdGhpcy5nZXN0dXJlc1tyZWNvZy5lbWl0c1tpXV0gPSByZWNvZzsKICAgICAgfQogICAgfSwKCiAgICAvKioKICAgICAqIEBwcml2YXRlCiAgICAgKiBAcGFyYW0ge3N0cmluZ30gZXZOYW1lIEV2ZW50IG5hbWUuCiAgICAgKiBAcmV0dXJuIHtPYmplY3R9IFJldHVybnMgdGhlIGdlc3R1cmUgZm9yIHRoZSBnaXZlbiBldmVudCBuYW1lLgogICAgICogQHRoaXMge0dlc3R1cmVzfQogICAgICovCiAgICBfZmluZFJlY29nbml6ZXJCeUV2ZW50OiBmdW5jdGlvbihldk5hbWUpIHsKICAgICAgZm9yIChsZXQgaSA9IDAsIHI7IGkgPCB0aGlzLnJlY29nbml6ZXJzLmxlbmd0aDsgaSsrKSB7CiAgICAgICAgciA9IHRoaXMucmVjb2duaXplcnNbaV07CiAgICAgICAgZm9yIChsZXQgaiA9IDAsIG47IGogPCByLmVtaXRzLmxlbmd0aDsgaisrKSB7CiAgICAgICAgICBuID0gci5lbWl0c1tqXTsKICAgICAgICAgIGlmIChuID09PSBldk5hbWUpIHsKICAgICAgICAgICAgcmV0dXJuIHI7CiAgICAgICAgICB9CiAgICAgICAgfQogICAgICB9CiAgICAgIHJldHVybiBudWxsOwogICAgfSwKCiAgICAvKioKICAgICAqIFNldHMgc2Nyb2xsaW5nIGRpcmVjdGlvbiBvbiBub2RlLgogICAgICoKICAgICAqIFRoaXMgdmFsdWUgaXMgY2hlY2tlZCBvbiBmaXJzdCBtb3ZlLCB0aHVzIGl0IHNob3VsZCBiZSBjYWxsZWQgcHJpb3IgdG8KICAgICAqIGFkZGluZyBldmVudCBsaXN0ZW5lcnMuCiAgICAgKgogICAgICogQG1lbWJlcm9mIFBvbHltZXIuR2VzdHVyZXMKICAgICAqIEBwYXJhbSB7IUVsZW1lbnR9IG5vZGUgTm9kZSB0byBzZXQgdG91Y2ggYWN0aW9uIHNldHRpbmcgb24KICAgICAqIEBwYXJhbSB7c3RyaW5nfSB2YWx1ZSBUb3VjaCBhY3Rpb24gdmFsdWUKICAgICAqIEByZXR1cm4ge3ZvaWR9CiAgICAgKi8KICAgIHNldFRvdWNoQWN0aW9uOiBmdW5jdGlvbihub2RlLCB2YWx1ZSkgewogICAgICBpZiAoSEFTX05BVElWRV9UQSkgewogICAgICAgIC8vIE5PVEU6IGFkZCB0b3VjaEFjdGlvbiBhc3luYyBzbyB0aGF0IGV2ZW50cyBjYW4gYmUgYWRkZWQgaW4KICAgICAgICAvLyBjdXN0b20gZWxlbWVudCBjb25zdHJ1Y3RvcnMuIE90aGVyd2lzZSB3ZSBydW4gYWZvdWwgb2YgY3VzdG9tCiAgICAgICAgLy8gZWxlbWVudHMgcmVzdHJpY3Rpb24gYWdhaW5zdCBzZXR0aW5ncyBhdHRyaWJ1dGVzIChzdHlsZSkgaW4gdGhlCiAgICAgICAgLy8gY29uc3RydWN0b3IuCiAgICAgICAgUG9seW1lci5Bc3luYy5taWNyb1Rhc2sucnVuKCgpID0+IHsKICAgICAgICAgIG5vZGUuc3R5bGUudG91Y2hBY3Rpb24gPSB2YWx1ZTsKICAgICAgICB9KTsKICAgICAgfQogICAgICBub2RlW1RPVUNIX0FDVElPTl0gPSB2YWx1ZTsKICAgIH0sCgogICAgLyoqCiAgICAgKiBEaXNwYXRjaGVzIGFuIGV2ZW50IG9uIHRoZSBgdGFyZ2V0YCBlbGVtZW50IG9mIGB0eXBlYCB3aXRoIHRoZSBnaXZlbgogICAgICogYGRldGFpbGAuCiAgICAgKiBAcHJpdmF0ZQogICAgICogQHBhcmFtIHshRXZlbnRUYXJnZXR9IHRhcmdldCBUaGUgZWxlbWVudCBvbiB3aGljaCB0byBmaXJlIGFuIGV2ZW50LgogICAgICogQHBhcmFtIHtzdHJpbmd9IHR5cGUgVGhlIHR5cGUgb2YgZXZlbnQgdG8gZmlyZS4KICAgICAqIEBwYXJhbSB7IU9iamVjdD19IGRldGFpbCBUaGUgZGV0YWlsIG9iamVjdCB0byBwb3B1bGF0ZSBvbiB0aGUgZXZlbnQuCiAgICAgKiBAcmV0dXJuIHt2b2lkfQogICAgICovCiAgICBfZmlyZTogZnVuY3Rpb24odGFyZ2V0LCB0eXBlLCBkZXRhaWwpIHsKICAgICAgbGV0IGV2ID0gbmV3IEV2ZW50KHR5cGUsIHsgYnViYmxlczogdHJ1ZSwgY2FuY2VsYWJsZTogdHJ1ZSwgY29tcG9zZWQ6IHRydWUgfSk7CiAgICAgIGV2LmRldGFpbCA9IGRldGFpbDsKICAgICAgdGFyZ2V0LmRpc3BhdGNoRXZlbnQoZXYpOwogICAgICAvLyBmb3J3YXJkIGBwcmV2ZW50RGVmYXVsdGAgaW4gYSBjbGVhbiB3YXkKICAgICAgaWYgKGV2LmRlZmF1bHRQcmV2ZW50ZWQpIHsKICAgICAgICBsZXQgcHJldmVudGVyID0gZGV0YWlsLnByZXZlbnRlciB8fCBkZXRhaWwuc291cmNlRXZlbnQ7CiAgICAgICAgaWYgKHByZXZlbnRlciAmJiBwcmV2ZW50ZXIucHJldmVudERlZmF1bHQpIHsKICAgICAgICAgIHByZXZlbnRlci5wcmV2ZW50RGVmYXVsdCgpOwogICAgICAgIH0KICAgICAgfQogICAgfSwKCiAgICAvKioKICAgICAqIFByZXZlbnRzIHRoZSBkaXNwYXRjaCBhbmQgZGVmYXVsdCBhY3Rpb24gb2YgdGhlIGdpdmVuIGV2ZW50IG5hbWUuCiAgICAgKgogICAgICogQG1lbWJlcm9mIFBvbHltZXIuR2VzdHVyZXMKICAgICAqIEBwYXJhbSB7c3RyaW5nfSBldk5hbWUgRXZlbnQgbmFtZS4KICAgICAqIEByZXR1cm4ge3ZvaWR9CiAgICAgKiBAdGhpcyB7R2VzdHVyZXN9CiAgICAgKi8KICAgIHByZXZlbnQ6IGZ1bmN0aW9uKGV2TmFtZSkgewogICAgICBsZXQgcmVjb2duaXplciA9IHRoaXMuX2ZpbmRSZWNvZ25pemVyQnlFdmVudChldk5hbWUpOwogICAgICBpZiAocmVjb2duaXplci5pbmZvKSB7CiAgICAgICAgcmVjb2duaXplci5pbmZvLnByZXZlbnQgPSB0cnVlOwogICAgICB9CiAgICB9LAoKICAgIC8qKgogICAgICogUmVzZXQgdGhlIDI1MDBtcyB0aW1lb3V0IG9uIHByb2Nlc3NpbmcgbW91c2UgaW5wdXQgYWZ0ZXIgZGV0ZWN0aW5nIHRvdWNoIGlucHV0LgogICAgICoKICAgICAqIFRvdWNoIGlucHV0cyBjcmVhdGUgc3ludGhlc2l6ZWQgbW91c2UgaW5wdXRzIGFueXdoZXJlIGZyb20gMCB0byAyMDAwbXMgYWZ0ZXIgdGhlIHRvdWNoLgogICAgICogVGhpcyBtZXRob2Qgc2hvdWxkIG9ubHkgYmUgY2FsbGVkIGR1cmluZyB0ZXN0aW5nIHdpdGggc2ltdWxhdGVkIHRvdWNoIGlucHV0cy4KICAgICAqIENhbGxpbmcgdGhpcyBtZXRob2QgaW4gcHJvZHVjdGlvbiBtYXkgY2F1c2UgZHVwbGljYXRlIHRhcHMgb3Igb3RoZXIgR2VzdHVyZXMuCiAgICAgKgogICAgICogQG1lbWJlcm9mIFBvbHltZXIuR2VzdHVyZXMKICAgICAqIEByZXR1cm4ge3ZvaWR9CiAgICAgKi8KICAgIHJlc2V0TW91c2VDYW5jZWxsZXI6IGZ1bmN0aW9uKCkgewogICAgICBpZiAoUE9JTlRFUlNUQVRFLm1vdXNlLm1vdXNlSWdub3JlSm9iKSB7CiAgICAgICAgUE9JTlRFUlNUQVRFLm1vdXNlLm1vdXNlSWdub3JlSm9iLmZsdXNoKCk7CiAgICAgIH0KICAgIH0KICB9OwoKICAvKiBlc2xpbnQtZGlzYWJsZSB2YWxpZC1qc2RvYyAqLwoKICBHZXN0dXJlcy5yZWdpc3Rlcih7CiAgICBuYW1lOiAnZG93bnVwJywKICAgIGRlcHM6IFsnbW91c2Vkb3duJywgJ3RvdWNoc3RhcnQnLCAndG91Y2hlbmQnXSwKICAgIGZsb3c6IHsKICAgICAgc3RhcnQ6IFsnbW91c2Vkb3duJywgJ3RvdWNoc3RhcnQnXSwKICAgICAgZW5kOiBbJ21vdXNldXAnLCAndG91Y2hlbmQnXQogICAgfSwKICAgIGVtaXRzOiBbJ2Rvd24nLCAndXAnXSwKCiAgICBpbmZvOiB7CiAgICAgIG1vdmVmbjogbnVsbCwKICAgICAgdXBmbjogbnVsbAogICAgfSwKCiAgICAvKioKICAgICAqIEB0aGlzIHtHZXN0dXJlUmVjb2duaXplcn0KICAgICAqIEByZXR1cm4ge3ZvaWR9CiAgICAgKi8KICAgIHJlc2V0OiBmdW5jdGlvbigpIHsKICAgICAgdW50cmFja0RvY3VtZW50KHRoaXMuaW5mbyk7CiAgICB9LAoKICAgIC8qKgogICAgICogQHRoaXMge0dlc3R1cmVSZWNvZ25pemVyfQogICAgICogQHBhcmFtIHtNb3VzZUV2ZW50fSBlCiAgICAgKiBAcmV0dXJuIHt2b2lkfQogICAgICovCiAgICBtb3VzZWRvd246IGZ1bmN0aW9uKGUpIHsKICAgICAgaWYgKCFoYXNMZWZ0TW91c2VCdXR0b24oZSkpIHsKICAgICAgICByZXR1cm47CiAgICAgIH0KICAgICAgbGV0IHQgPSBHZXN0dXJlcy5fZmluZE9yaWdpbmFsVGFyZ2V0KGUpOwogICAgICBsZXQgc2VsZiA9IHRoaXM7CiAgICAgIGxldCBtb3ZlZm4gPSBmdW5jdGlvbiBtb3ZlZm4oZSkgewogICAgICAgIGlmICghaGFzTGVmdE1vdXNlQnV0dG9uKGUpKSB7CiAgICAgICAgICBzZWxmLl9maXJlKCd1cCcsIHQsIGUpOwogICAgICAgICAgdW50cmFja0RvY3VtZW50KHNlbGYuaW5mbyk7CiAgICAgICAgfQogICAgICB9OwogICAgICBsZXQgdXBmbiA9IGZ1bmN0aW9uIHVwZm4oZSkgewogICAgICAgIGlmIChoYXNMZWZ0TW91c2VCdXR0b24oZSkpIHsKICAgICAgICAgIHNlbGYuX2ZpcmUoJ3VwJywgdCwgZSk7CiAgICAgICAgfQogICAgICAgIHVudHJhY2tEb2N1bWVudChzZWxmLmluZm8pOwogICAgICB9OwogICAgICB0cmFja0RvY3VtZW50KHRoaXMuaW5mbywgbW92ZWZuLCB1cGZuKTsKICAgICAgdGhpcy5fZmlyZSgnZG93bicsIHQsIGUpOwogICAgfSwKICAgIC8qKgogICAgICogQHRoaXMge0dlc3R1cmVSZWNvZ25pemVyfQogICAgICogQHBhcmFtIHtUb3VjaEV2ZW50fSBlCiAgICAgKiBAcmV0dXJuIHt2b2lkfQogICAgICovCiAgICB0b3VjaHN0YXJ0OiBmdW5jdGlvbihlKSB7CiAgICAgIHRoaXMuX2ZpcmUoJ2Rvd24nLCBHZXN0dXJlcy5fZmluZE9yaWdpbmFsVGFyZ2V0KGUpLCBlLmNoYW5nZWRUb3VjaGVzWzBdLCBlKTsKICAgIH0sCiAgICAvKioKICAgICAqIEB0aGlzIHtHZXN0dXJlUmVjb2duaXplcn0KICAgICAqIEBwYXJhbSB7VG91Y2hFdmVudH0gZQogICAgICogQHJldHVybiB7dm9pZH0KICAgICAqLwogICAgdG91Y2hlbmQ6IGZ1bmN0aW9uKGUpIHsKICAgICAgdGhpcy5fZmlyZSgndXAnLCBHZXN0dXJlcy5fZmluZE9yaWdpbmFsVGFyZ2V0KGUpLCBlLmNoYW5nZWRUb3VjaGVzWzBdLCBlKTsKICAgIH0sCiAgICAvKioKICAgICAqIEBwYXJhbSB7c3RyaW5nfSB0eXBlCiAgICAgKiBAcGFyYW0geyFFdmVudFRhcmdldH0gdGFyZ2V0CiAgICAgKiBAcGFyYW0ge0V2ZW50fSBldmVudAogICAgICogQHBhcmFtIHtGdW5jdGlvbn0gcHJldmVudGVyCiAgICAgKiBAcmV0dXJuIHt2b2lkfQogICAgICovCiAgICBfZmlyZTogZnVuY3Rpb24odHlwZSwgdGFyZ2V0LCBldmVudCwgcHJldmVudGVyKSB7CiAgICAgIEdlc3R1cmVzLl9maXJlKHRhcmdldCwgdHlwZSwgewogICAgICAgIHg6IGV2ZW50LmNsaWVudFgsCiAgICAgICAgeTogZXZlbnQuY2xpZW50WSwKICAgICAgICBzb3VyY2VFdmVudDogZXZlbnQsCiAgICAgICAgcHJldmVudGVyOiBwcmV2ZW50ZXIsCiAgICAgICAgcHJldmVudDogZnVuY3Rpb24oZSkgewogICAgICAgICAgcmV0dXJuIEdlc3R1cmVzLnByZXZlbnQoZSk7CiAgICAgICAgfQogICAgICB9KTsKICAgIH0KICB9KTsKCiAgR2VzdHVyZXMucmVnaXN0ZXIoewogICAgbmFtZTogJ3RyYWNrJywKICAgIHRvdWNoQWN0aW9uOiAnbm9uZScsCiAgICBkZXBzOiBbJ21vdXNlZG93bicsICd0b3VjaHN0YXJ0JywgJ3RvdWNobW92ZScsICd0b3VjaGVuZCddLAogICAgZmxvdzogewogICAgICBzdGFydDogWydtb3VzZWRvd24nLCAndG91Y2hzdGFydCddLAogICAgICBlbmQ6IFsnbW91c2V1cCcsICd0b3VjaGVuZCddCiAgICB9LAogICAgZW1pdHM6IFsndHJhY2snXSwKCiAgICBpbmZvOiB7CiAgICAgIHg6IDAsCiAgICAgIHk6IDAsCiAgICAgIHN0YXRlOiAnc3RhcnQnLAogICAgICBzdGFydGVkOiBmYWxzZSwKICAgICAgbW92ZXM6IFtdLAogICAgICAvKiogQHRoaXMge0dlc3R1cmVSZWNvZ25pemVyfSAqLwogICAgICBhZGRNb3ZlOiBmdW5jdGlvbihtb3ZlKSB7CiAgICAgICAgaWYgKHRoaXMubW92ZXMubGVuZ3RoID4gVFJBQ0tfTEVOR1RIKSB7CiAgICAgICAgICB0aGlzLm1vdmVzLnNoaWZ0KCk7CiAgICAgICAgfQogICAgICAgIHRoaXMubW92ZXMucHVzaChtb3ZlKTsKICAgICAgfSwKICAgICAgbW92ZWZuOiBudWxsLAogICAgICB1cGZuOiBudWxsLAogICAgICBwcmV2ZW50OiBmYWxzZQogICAgfSwKCiAgICAvKioKICAgICAqIEB0aGlzIHtHZXN0dXJlUmVjb2duaXplcn0KICAgICAqIEByZXR1cm4ge3ZvaWR9CiAgICAgKi8KICAgIHJlc2V0OiBmdW5jdGlvbigpIHsKICAgICAgdGhpcy5pbmZvLnN0YXRlID0gJ3N0YXJ0JzsKICAgICAgdGhpcy5pbmZvLnN0YXJ0ZWQgPSBmYWxzZTsKICAgICAgdGhpcy5pbmZvLm1vdmVzID0gW107CiAgICAgIHRoaXMuaW5mby54ID0gMDsKICAgICAgdGhpcy5pbmZvLnkgPSAwOwogICAgICB0aGlzLmluZm8ucHJldmVudCA9IGZhbHNlOwogICAgICB1bnRyYWNrRG9jdW1lbnQodGhpcy5pbmZvKTsKICAgIH0sCgogICAgLyoqCiAgICAgKiBAdGhpcyB7R2VzdHVyZVJlY29nbml6ZXJ9CiAgICAgKiBAcGFyYW0ge251bWJlcn0geAogICAgICogQHBhcmFtIHtudW1iZXJ9IHkKICAgICAqIEByZXR1cm4ge2Jvb2xlYW59CiAgICAgKi8KICAgIGhhc01vdmVkRW5vdWdoOiBmdW5jdGlvbih4LCB5KSB7CiAgICAgIGlmICh0aGlzLmluZm8ucHJldmVudCkgewogICAgICAgIHJldHVybiBmYWxzZTsKICAgICAgfQogICAgICBpZiAodGhpcy5pbmZvLnN0YXJ0ZWQpIHsKICAgICAgICByZXR1cm4gdHJ1ZTsKICAgICAgfQogICAgICBsZXQgZHggPSBNYXRoLmFicyh0aGlzLmluZm8ueCAtIHgpOwogICAgICBsZXQgZHkgPSBNYXRoLmFicyh0aGlzLmluZm8ueSAtIHkpOwogICAgICByZXR1cm4gKGR4ID49IFRSQUNLX0RJU1RBTkNFIHx8IGR5ID49IFRSQUNLX0RJU1RBTkNFKTsKICAgIH0sCiAgICAvKioKICAgICAqIEB0aGlzIHtHZXN0dXJlUmVjb2duaXplcn0KICAgICAqIEBwYXJhbSB7TW91c2VFdmVudH0gZQogICAgICogQHJldHVybiB7dm9pZH0KICAgICAqLwogICAgbW91c2Vkb3duOiBmdW5jdGlvbihlKSB7CiAgICAgIGlmICghaGFzTGVmdE1vdXNlQnV0dG9uKGUpKSB7CiAgICAgICAgcmV0dXJuOwogICAgICB9CiAgICAgIGxldCB0ID0gR2VzdHVyZXMuX2ZpbmRPcmlnaW5hbFRhcmdldChlKTsKICAgICAgbGV0IHNlbGYgPSB0aGlzOwogICAgICBsZXQgbW92ZWZuID0gZnVuY3Rpb24gbW92ZWZuKGUpIHsKICAgICAgICBsZXQgeCA9IGUuY2xpZW50WCwgeSA9IGUuY2xpZW50WTsKICAgICAgICBpZiAoc2VsZi5oYXNNb3ZlZEVub3VnaCh4LCB5KSkgewogICAgICAgICAgLy8gZmlyc3QgbW92ZSBpcyAnc3RhcnQnLCBzdWJzZXF1ZW50IG1vdmVzIGFyZSAnbW92ZScsIG1vdXNldXAgaXMgJ2VuZCcKICAgICAgICAgIHNlbGYuaW5mby5zdGF0ZSA9IHNlbGYuaW5mby5zdGFydGVkID8gKGUudHlwZSA9PT0gJ21vdXNldXAnID8gJ2VuZCcgOiAndHJhY2snKSA6ICdzdGFydCc7CiAgICAgICAgICBpZiAoc2VsZi5pbmZvLnN0YXRlID09PSAnc3RhcnQnKSB7CiAgICAgICAgICAgIC8vIGlmIGFuZCBvbmx5IGlmIHRyYWNraW5nLCBhbHdheXMgcHJldmVudCB0YXAKICAgICAgICAgICAgR2VzdHVyZXMucHJldmVudCgndGFwJyk7CiAgICAgICAgICB9CiAgICAgICAgICBzZWxmLmluZm8uYWRkTW92ZSh7eDogeCwgeTogeX0pOwogICAgICAgICAgaWYgKCFoYXNMZWZ0TW91c2VCdXR0b24oZSkpIHsKICAgICAgICAgICAgLy8gYWx3YXlzIF9maXJlICJlbmQiCiAgICAgICAgICAgIHNlbGYuaW5mby5zdGF0ZSA9ICdlbmQnOwogICAgICAgICAgICB1bnRyYWNrRG9jdW1lbnQoc2VsZi5pbmZvKTsKICAgICAgICAgIH0KICAgICAgICAgIHNlbGYuX2ZpcmUodCwgZSk7CiAgICAgICAgICBzZWxmLmluZm8uc3RhcnRlZCA9IHRydWU7CiAgICAgICAgfQogICAgICB9OwogICAgICBsZXQgdXBmbiA9IGZ1bmN0aW9uIHVwZm4oZSkgewogICAgICAgIGlmIChzZWxmLmluZm8uc3RhcnRlZCkgewogICAgICAgICAgbW92ZWZuKGUpOwogICAgICAgIH0KCiAgICAgICAgLy8gcmVtb3ZlIHRoZSB0ZW1wb3JhcnkgbGlzdGVuZXJzCiAgICAgICAgdW50cmFja0RvY3VtZW50KHNlbGYuaW5mbyk7CiAgICAgIH07CiAgICAgIC8vIGFkZCB0ZW1wb3JhcnkgZG9jdW1lbnQgbGlzdGVuZXJzIGFzIG1vdXNlIHJldGFyZ2V0cwogICAgICB0cmFja0RvY3VtZW50KHRoaXMuaW5mbywgbW92ZWZuLCB1cGZuKTsKICAgICAgdGhpcy5pbmZvLnggPSBlLmNsaWVudFg7CiAgICAgIHRoaXMuaW5mby55ID0gZS5jbGllbnRZOwogICAgfSwKICAgIC8qKgogICAgICogQHRoaXMge0dlc3R1cmVSZWNvZ25pemVyfQogICAgICogQHBhcmFtIHtUb3VjaEV2ZW50fSBlCiAgICAgKiBAcmV0dXJuIHt2b2lkfQogICAgICovCiAgICB0b3VjaHN0YXJ0OiBmdW5jdGlvbihlKSB7CiAgICAgIGxldCBjdCA9IGUuY2hhbmdlZFRvdWNoZXNbMF07CiAgICAgIHRoaXMuaW5mby54ID0gY3QuY2xpZW50WDsKICAgICAgdGhpcy5pbmZvLnkgPSBjdC5jbGllbnRZOwogICAgfSwKICAgIC8qKgogICAgICogQHRoaXMge0dlc3R1cmVSZWNvZ25pemVyfQogICAgICogQHBhcmFtIHtUb3VjaEV2ZW50fSBlCiAgICAgKiBAcmV0dXJuIHt2b2lkfQogICAgICovCiAgICB0b3VjaG1vdmU6IGZ1bmN0aW9uKGUpIHsKICAgICAgbGV0IHQgPSBHZXN0dXJlcy5fZmluZE9yaWdpbmFsVGFyZ2V0KGUpOwogICAgICBsZXQgY3QgPSBlLmNoYW5nZWRUb3VjaGVzWzBdOwogICAgICBsZXQgeCA9IGN0LmNsaWVudFgsIHkgPSBjdC5jbGllbnRZOwogICAgICBpZiAodGhpcy5oYXNNb3ZlZEVub3VnaCh4LCB5KSkgewogICAgICAgIGlmICh0aGlzLmluZm8uc3RhdGUgPT09ICdzdGFydCcpIHsKICAgICAgICAgIC8vIGlmIGFuZCBvbmx5IGlmIHRyYWNraW5nLCBhbHdheXMgcHJldmVudCB0YXAKICAgICAgICAgIEdlc3R1cmVzLnByZXZlbnQoJ3RhcCcpOwogICAgICAgIH0KICAgICAgICB0aGlzLmluZm8uYWRkTW92ZSh7eDogeCwgeTogeX0pOwogICAgICAgIHRoaXMuX2ZpcmUodCwgY3QpOwogICAgICAgIHRoaXMuaW5mby5zdGF0ZSA9ICd0cmFjayc7CiAgICAgICAgdGhpcy5pbmZvLnN0YXJ0ZWQgPSB0cnVlOwogICAgICB9CiAgICB9LAogICAgLyoqCiAgICAgKiBAdGhpcyB7R2VzdHVyZVJlY29nbml6ZXJ9CiAgICAgKiBAcGFyYW0ge1RvdWNoRXZlbnR9IGUKICAgICAqIEByZXR1cm4ge3ZvaWR9CiAgICAgKi8KICAgIHRvdWNoZW5kOiBmdW5jdGlvbihlKSB7CiAgICAgIGxldCB0ID0gR2VzdHVyZXMuX2ZpbmRPcmlnaW5hbFRhcmdldChlKTsKICAgICAgbGV0IGN0ID0gZS5jaGFuZ2VkVG91Y2hlc1swXTsKICAgICAgLy8gb25seSB0cmFja2VuZCBpZiB0cmFjayB3YXMgc3RhcnRlZCBhbmQgbm90IGFib3J0ZWQKICAgICAgaWYgKHRoaXMuaW5mby5zdGFydGVkKSB7CiAgICAgICAgLy8gcmVzZXQgc3RhcnRlZCBzdGF0ZSBvbiB1cAogICAgICAgIHRoaXMuaW5mby5zdGF0ZSA9ICdlbmQnOwogICAgICAgIHRoaXMuaW5mby5hZGRNb3ZlKHt4OiBjdC5jbGllbnRYLCB5OiBjdC5jbGllbnRZfSk7CiAgICAgICAgdGhpcy5fZmlyZSh0LCBjdCwgZSk7CiAgICAgIH0KICAgIH0sCgogICAgLyoqCiAgICAgKiBAdGhpcyB7R2VzdHVyZVJlY29nbml6ZXJ9CiAgICAgKiBAcGFyYW0geyFFdmVudFRhcmdldH0gdGFyZ2V0CiAgICAgKiBAcGFyYW0ge1RvdWNofSB0b3VjaAogICAgICogQHJldHVybiB7dm9pZH0KICAgICAqLwogICAgX2ZpcmU6IGZ1bmN0aW9uKHRhcmdldCwgdG91Y2gpIHsKICAgICAgbGV0IHNlY29uZGxhc3QgPSB0aGlzLmluZm8ubW92ZXNbdGhpcy5pbmZvLm1vdmVzLmxlbmd0aCAtIDJdOwogICAgICBsZXQgbGFzdG1vdmUgPSB0aGlzLmluZm8ubW92ZXNbdGhpcy5pbmZvLm1vdmVzLmxlbmd0aCAtIDFdOwogICAgICBsZXQgZHggPSBsYXN0bW92ZS54IC0gdGhpcy5pbmZvLng7CiAgICAgIGxldCBkeSA9IGxhc3Rtb3ZlLnkgLSB0aGlzLmluZm8ueTsKICAgICAgbGV0IGRkeCwgZGR5ID0gMDsKICAgICAgaWYgKHNlY29uZGxhc3QpIHsKICAgICAgICBkZHggPSBsYXN0bW92ZS54IC0gc2Vjb25kbGFzdC54OwogICAgICAgIGRkeSA9IGxhc3Rtb3ZlLnkgLSBzZWNvbmRsYXN0Lnk7CiAgICAgIH0KICAgICAgR2VzdHVyZXMuX2ZpcmUodGFyZ2V0LCAndHJhY2snLCB7CiAgICAgICAgc3RhdGU6IHRoaXMuaW5mby5zdGF0ZSwKICAgICAgICB4OiB0b3VjaC5jbGllbnRYLAogICAgICAgIHk6IHRvdWNoLmNsaWVudFksCiAgICAgICAgZHg6IGR4LAogICAgICAgIGR5OiBkeSwKICAgICAgICBkZHg6IGRkeCwKICAgICAgICBkZHk6IGRkeSwKICAgICAgICBzb3VyY2VFdmVudDogdG91Y2gsCiAgICAgICAgaG92ZXI6IGZ1bmN0aW9uKCkgewogICAgICAgICAgcmV0dXJuIEdlc3R1cmVzLmRlZXBUYXJnZXRGaW5kKHRvdWNoLmNsaWVudFgsIHRvdWNoLmNsaWVudFkpOwogICAgICAgIH0KICAgICAgfSk7CiAgICB9CgogIH0pOwoKICBHZXN0dXJlcy5yZWdpc3Rlcih7CiAgICBuYW1lOiAndGFwJywKICAgIGRlcHM6IFsnbW91c2Vkb3duJywgJ2NsaWNrJywgJ3RvdWNoc3RhcnQnLCAndG91Y2hlbmQnXSwKICAgIGZsb3c6IHsKICAgICAgc3RhcnQ6IFsnbW91c2Vkb3duJywgJ3RvdWNoc3RhcnQnXSwKICAgICAgZW5kOiBbJ2NsaWNrJywgJ3RvdWNoZW5kJ10KICAgIH0sCiAgICBlbWl0czogWyd0YXAnXSwKICAgIGluZm86IHsKICAgICAgeDogTmFOLAogICAgICB5OiBOYU4sCiAgICAgIHByZXZlbnQ6IGZhbHNlCiAgICB9LAogICAgLyoqCiAgICAgKiBAdGhpcyB7R2VzdHVyZVJlY29nbml6ZXJ9CiAgICAgKiBAcmV0dXJuIHt2b2lkfQogICAgICovCiAgICByZXNldDogZnVuY3Rpb24oKSB7CiAgICAgIHRoaXMuaW5mby54ID0gTmFOOwogICAgICB0aGlzLmluZm8ueSA9IE5hTjsKICAgICAgdGhpcy5pbmZvLnByZXZlbnQgPSBmYWxzZTsKICAgIH0sCiAgICAvKioKICAgICAqIEB0aGlzIHtHZXN0dXJlUmVjb2duaXplcn0KICAgICAqIEBwYXJhbSB7TW91c2VFdmVudH0gZQogICAgICogQHJldHVybiB7dm9pZH0KICAgICAqLwogICAgc2F2ZTogZnVuY3Rpb24oZSkgewogICAgICB0aGlzLmluZm8ueCA9IGUuY2xpZW50WDsKICAgICAgdGhpcy5pbmZvLnkgPSBlLmNsaWVudFk7CiAgICB9LAogICAgLyoqCiAgICAgKiBAdGhpcyB7R2VzdHVyZVJlY29nbml6ZXJ9CiAgICAgKiBAcGFyYW0ge01vdXNlRXZlbnR9IGUKICAgICAqIEByZXR1cm4ge3ZvaWR9CiAgICAgKi8KICAgIG1vdXNlZG93bjogZnVuY3Rpb24oZSkgewogICAgICBpZiAoaGFzTGVmdE1vdXNlQnV0dG9uKGUpKSB7CiAgICAgICAgdGhpcy5zYXZlKGUpOwogICAgICB9CiAgICB9LAogICAgLyoqCiAgICAgKiBAdGhpcyB7R2VzdHVyZVJlY29nbml6ZXJ9CiAgICAgKiBAcGFyYW0ge01vdXNlRXZlbnR9IGUKICAgICAqIEByZXR1cm4ge3ZvaWR9CiAgICAgKi8KICAgIGNsaWNrOiBmdW5jdGlvbihlKSB7CiAgICAgIGlmIChoYXNMZWZ0TW91c2VCdXR0b24oZSkpIHsKICAgICAgICB0aGlzLmZvcndhcmQoZSk7CiAgICAgIH0KICAgIH0sCiAgICAvKioKICAgICAqIEB0aGlzIHtHZXN0dXJlUmVjb2duaXplcn0KICAgICAqIEBwYXJhbSB7VG91Y2hFdmVudH0gZQogICAgICogQHJldHVybiB7dm9pZH0KICAgICAqLwogICAgdG91Y2hzdGFydDogZnVuY3Rpb24oZSkgewogICAgICB0aGlzLnNhdmUoZS5jaGFuZ2VkVG91Y2hlc1swXSwgZSk7CiAgICB9LAogICAgLyoqCiAgICAgKiBAdGhpcyB7R2VzdHVyZVJlY29nbml6ZXJ9CiAgICAgKiBAcGFyYW0ge1RvdWNoRXZlbnR9IGUKICAgICAqIEByZXR1cm4ge3ZvaWR9CiAgICAgKi8KICAgIHRvdWNoZW5kOiBmdW5jdGlvbihlKSB7CiAgICAgIHRoaXMuZm9yd2FyZChlLmNoYW5nZWRUb3VjaGVzWzBdLCBlKTsKICAgIH0sCiAgICAvKioKICAgICAqIEB0aGlzIHtHZXN0dXJlUmVjb2duaXplcn0KICAgICAqIEBwYXJhbSB7RXZlbnQgfCBUb3VjaH0gZQogICAgICogQHBhcmFtIHtFdmVudD19IHByZXZlbnRlcgogICAgICogQHJldHVybiB7dm9pZH0KICAgICAqLwogICAgZm9yd2FyZDogZnVuY3Rpb24oZSwgcHJldmVudGVyKSB7CiAgICAgIGxldCBkeCA9IE1hdGguYWJzKGUuY2xpZW50WCAtIHRoaXMuaW5mby54KTsKICAgICAgbGV0IGR5ID0gTWF0aC5hYnMoZS5jbGllbnRZIC0gdGhpcy5pbmZvLnkpOwogICAgICAvLyBmaW5kIG9yaWdpbmFsIHRhcmdldCBmcm9tIGBwcmV2ZW50ZXJgIGZvciBUb3VjaEV2ZW50cywgb3IgYGVgIGZvciBNb3VzZUV2ZW50cwogICAgICBsZXQgdCA9IEdlc3R1cmVzLl9maW5kT3JpZ2luYWxUYXJnZXQoLyoqIEB0eXBlIHtFdmVudH0gKi8ocHJldmVudGVyIHx8IGUpKTsKICAgICAgaWYgKCF0IHx8IChjYW5CZURpc2FibGVkWy8qKiBAdHlwZSB7IUhUTUxFbGVtZW50fSAqLyh0KS5sb2NhbE5hbWVdICYmIHQuaGFzQXR0cmlidXRlKCdkaXNhYmxlZCcpKSkgewogICAgICAgIHJldHVybjsKICAgICAgfQogICAgICAvLyBkeCxkeSBjYW4gYmUgTmFOIGlmIGBjbGlja2AgaGFzIGJlZW4gc2ltdWxhdGVkIGFuZCB0aGVyZSB3YXMgbm8gYGRvd25gIGZvciBgc3RhcnRgCiAgICAgIGlmIChpc05hTihkeCkgfHwgaXNOYU4oZHkpIHx8IChkeCA8PSBUQVBfRElTVEFOQ0UgJiYgZHkgPD0gVEFQX0RJU1RBTkNFKSB8fCBpc1N5bnRoZXRpY0NsaWNrKGUpKSB7CiAgICAgICAgLy8gcHJldmVudCB0YXBzIGZyb20gYmVpbmcgZ2VuZXJhdGVkIGlmIGFuIGV2ZW50IGhhcyBjYW5jZWxlZCB0aGVtCiAgICAgICAgaWYgKCF0aGlzLmluZm8ucHJldmVudCkgewogICAgICAgICAgR2VzdHVyZXMuX2ZpcmUodCwgJ3RhcCcsIHsKICAgICAgICAgICAgeDogZS5jbGllbnRYLAogICAgICAgICAgICB5OiBlLmNsaWVudFksCiAgICAgICAgICAgIHNvdXJjZUV2ZW50OiBlLAogICAgICAgICAgICBwcmV2ZW50ZXI6IHByZXZlbnRlcgogICAgICAgICAgfSk7CiAgICAgICAgfQogICAgICB9CiAgICB9CiAgfSk7CgogIC8qIGVzbGludC1lbmFibGUgdmFsaWQtanNkb2MgKi8KCiAgLyoqIEBkZXByZWNhdGVkICovCiAgR2VzdHVyZXMuZmluZE9yaWdpbmFsVGFyZ2V0ID0gR2VzdHVyZXMuX2ZpbmRPcmlnaW5hbFRhcmdldDsKCiAgLyoqIEBkZXByZWNhdGVkICovCiAgR2VzdHVyZXMuYWRkID0gR2VzdHVyZXMuYWRkTGlzdGVuZXI7CgogIC8qKiBAZGVwcmVjYXRlZCAqLwogIEdlc3R1cmVzLnJlbW92ZSA9IEdlc3R1cmVzLnJlbW92ZUxpc3RlbmVyOwoKICBQb2x5bWVyLkdlc3R1cmVzID0gR2VzdHVyZXM7Cgp9KSgpOwo8L3NjcmlwdD48c2NyaXB0PgooZnVuY3Rpb24oKSB7CgogICd1c2Ugc3RyaWN0JzsKCiAgLyoqCiAgICogQGNvbnN0IHtQb2x5bWVyLkdlc3R1cmVzfQogICAqLwogIGNvbnN0IGdlc3R1cmVzID0gUG9seW1lci5HZXN0dXJlczsKCiAgLyoqCiAgICogRWxlbWVudCBjbGFzcyBtaXhpbiB0aGF0IHByb3ZpZGVzIEFQSSBmb3IgYWRkaW5nIFBvbHltZXIncyBjcm9zcy1wbGF0Zm9ybQogICAqIGdlc3R1cmUgZXZlbnRzIHRvIG5vZGVzLgogICAqCiAgICogVGhlIEFQSSBpcyBkZXNpZ25lZCB0byBiZSBjb21wYXRpYmxlIHdpdGggb3ZlcnJpZGUgcG9pbnRzIGltcGxlbWVudGVkCiAgICogaW4gYFBvbHltZXIuVGVtcGxhdGVTdGFtcGAgc3VjaCB0aGF0IGRlY2xhcmF0aXZlIGV2ZW50IGxpc3RlbmVycyBpbgogICAqIHRlbXBsYXRlcyB3aWxsIHN1cHBvcnQgZ2VzdHVyZSBldmVudHMgd2hlbiB0aGlzIG1peGluIGlzIGFwcGxpZWQgYWxvbmcgd2l0aAogICAqIGBQb2x5bWVyLlRlbXBsYXRlU3RhbXBgLgogICAqCiAgICogQG1peGluRnVuY3Rpb24KICAgKiBAcG9seW1lcgogICAqIEBtZW1iZXJvZiBQb2x5bWVyCiAgICogQHN1bW1hcnkgRWxlbWVudCBjbGFzcyBtaXhpbiB0aGF0IHByb3ZpZGVzIEFQSSBmb3IgYWRkaW5nIFBvbHltZXIncyBjcm9zcy1wbGF0Zm9ybQogICAqIGdlc3R1cmUgZXZlbnRzIHRvIG5vZGVzCiAgICovCiAgUG9seW1lci5HZXN0dXJlRXZlbnRMaXN0ZW5lcnMgPSBQb2x5bWVyLmRlZHVwaW5nTWl4aW4oc3VwZXJDbGFzcyA9PiB7CgogICAgLyoqCiAgICAgKiBAcG9seW1lcgogICAgICogQG1peGluQ2xhc3MKICAgICAqIEBpbXBsZW1lbnRzIHtQb2x5bWVyX0dlc3R1cmVFdmVudExpc3RlbmVyc30KICAgICAqLwogICAgY2xhc3MgR2VzdHVyZUV2ZW50TGlzdGVuZXJzIGV4dGVuZHMgc3VwZXJDbGFzcyB7CgogICAgICAvKioKICAgICAgICogQWRkIHRoZSBldmVudCBsaXN0ZW5lciB0byB0aGUgbm9kZSBpZiBpdCBpcyBhIGdlc3R1cmVzIGV2ZW50LgogICAgICAgKgogICAgICAgKiBAcGFyYW0geyFOb2RlfSBub2RlIE5vZGUgdG8gYWRkIGV2ZW50IGxpc3RlbmVyIHRvCiAgICAgICAqIEBwYXJhbSB7c3RyaW5nfSBldmVudE5hbWUgTmFtZSBvZiBldmVudAogICAgICAgKiBAcGFyYW0ge2Z1bmN0aW9uKCFFdmVudCk6dm9pZH0gaGFuZGxlciBMaXN0ZW5lciBmdW5jdGlvbiB0byBhZGQKICAgICAgICogQHJldHVybiB7dm9pZH0KICAgICAgICovCiAgICAgIF9hZGRFdmVudExpc3RlbmVyVG9Ob2RlKG5vZGUsIGV2ZW50TmFtZSwgaGFuZGxlcikgewogICAgICAgIGlmICghZ2VzdHVyZXMuYWRkTGlzdGVuZXIobm9kZSwgZXZlbnROYW1lLCBoYW5kbGVyKSkgewogICAgICAgICAgc3VwZXIuX2FkZEV2ZW50TGlzdGVuZXJUb05vZGUobm9kZSwgZXZlbnROYW1lLCBoYW5kbGVyKTsKICAgICAgICB9CiAgICAgIH0KCiAgICAgIC8qKgogICAgICAgKiBSZW1vdmUgdGhlIGV2ZW50IGxpc3RlbmVyIHRvIHRoZSBub2RlIGlmIGl0IGlzIGEgZ2VzdHVyZXMgZXZlbnQuCiAgICAgICAqCiAgICAgICAqIEBwYXJhbSB7IU5vZGV9IG5vZGUgTm9kZSB0byByZW1vdmUgZXZlbnQgbGlzdGVuZXIgZnJvbQogICAgICAgKiBAcGFyYW0ge3N0cmluZ30gZXZlbnROYW1lIE5hbWUgb2YgZXZlbnQKICAgICAgICogQHBhcmFtIHtmdW5jdGlvbighRXZlbnQpOnZvaWR9IGhhbmRsZXIgTGlzdGVuZXIgZnVuY3Rpb24gdG8gcmVtb3ZlCiAgICAgICAqIEByZXR1cm4ge3ZvaWR9CiAgICAgICAqLwogICAgICBfcmVtb3ZlRXZlbnRMaXN0ZW5lckZyb21Ob2RlKG5vZGUsIGV2ZW50TmFtZSwgaGFuZGxlcikgewogICAgICAgIGlmICghZ2VzdHVyZXMucmVtb3ZlTGlzdGVuZXIobm9kZSwgZXZlbnROYW1lLCBoYW5kbGVyKSkgewogICAgICAgICAgc3VwZXIuX3JlbW92ZUV2ZW50TGlzdGVuZXJGcm9tTm9kZShub2RlLCBldmVudE5hbWUsIGhhbmRsZXIpOwogICAgICAgIH0KICAgICAgfQoKICAgIH0KCiAgICByZXR1cm4gR2VzdHVyZUV2ZW50TGlzdGVuZXJzOwoKICB9KTsKCn0pKCk7Cjwvc2NyaXB0PjxzY3JpcHQ+CiAgKGZ1bmN0aW9uKCkgewogICAgJ3VzZSBzdHJpY3QnOwoKICAgIGNvbnN0IEhPU1RfRElSID0gLzpob3N0XCg6ZGlyXCgobHRyfHJ0bClcKVwpL2c7CiAgICBjb25zdCBIT1NUX0RJUl9SRVBMQUNNRU5UID0gJzpob3N0KFtkaXI9IiQxIl0pJzsKCiAgICBjb25zdCBFTF9ESVIgPSAvKFtcc1x3LSNcLlxbXF1cKl0qKTpkaXJcKChsdHJ8cnRsKVwpL2c7CiAgICBjb25zdCBFTF9ESVJfUkVQTEFDTUVOVCA9ICc6aG9zdChbZGlyPSIkMiJdKSAkMSc7CgogICAgY29uc3QgRElSX0NIRUNLID0gLzpkaXJcKCg/Omx0cnxydGwpXCkvOwogICAgCiAgICBjb25zdCBTSElNX1NIQURPVyA9IEJvb2xlYW4od2luZG93WydTaGFkeURPTSddICYmIHdpbmRvd1snU2hhZHlET00nXVsnaW5Vc2UnXSk7CgogICAgLyoqCiAgICAgKiBAdHlwZSB7IUFycmF5PCFQb2x5bWVyX0Rpck1peGluPn0KICAgICAqLwogICAgY29uc3QgRElSX0lOU1RBTkNFUyA9IFtdOwoKICAgIC8qKiBAdHlwZSB7TXV0YXRpb25PYnNlcnZlcn0gKi8KICAgIGxldCBvYnNlcnZlciA9IG51bGw7CgogICAgbGV0IERPQ1VNRU5UX0RJUiA9ICcnOwoKICAgIGZ1bmN0aW9uIGdldFJUTCgpIHsKICAgICAgRE9DVU1FTlRfRElSID0gZG9jdW1lbnQuZG9jdW1lbnRFbGVtZW50LmdldEF0dHJpYnV0ZSgnZGlyJyk7CiAgICB9CgogICAgLyoqCiAgICAgKiBAcGFyYW0geyFQb2x5bWVyX0Rpck1peGlufSBpbnN0YW5jZSBJbnN0YW5jZSB0byBzZXQgUlRMIHN0YXR1cyBvbgogICAgICovCiAgICBmdW5jdGlvbiBzZXRSVEwoaW5zdGFuY2UpIHsKICAgICAgaWYgKCFpbnN0YW5jZS5fX2F1dG9EaXJPcHRPdXQpIHsKICAgICAgICBjb25zdCBlbCA9IC8qKiBAdHlwZSB7IUhUTUxFbGVtZW50fSAqLyhpbnN0YW5jZSk7CiAgICAgICAgZWwuc2V0QXR0cmlidXRlKCdkaXInLCBET0NVTUVOVF9ESVIpOwogICAgICB9CiAgICB9CgogICAgZnVuY3Rpb24gdXBkYXRlRGlyZWN0aW9uKCkgewogICAgICBnZXRSVEwoKTsKICAgICAgRE9DVU1FTlRfRElSID0gZG9jdW1lbnQuZG9jdW1lbnRFbGVtZW50LmdldEF0dHJpYnV0ZSgnZGlyJyk7CiAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgRElSX0lOU1RBTkNFUy5sZW5ndGg7IGkrKykgewogICAgICAgIHNldFJUTChESVJfSU5TVEFOQ0VTW2ldKTsKICAgICAgfQogICAgfQoKICAgIGZ1bmN0aW9uIHRha2VSZWNvcmRzKCkgewogICAgICBpZiAob2JzZXJ2ZXIgJiYgb2JzZXJ2ZXIudGFrZVJlY29yZHMoKS5sZW5ndGgpIHsKICAgICAgICB1cGRhdGVEaXJlY3Rpb24oKTsKICAgICAgfQogICAgfQoKICAgIC8qKgogICAgICogRWxlbWVudCBjbGFzcyBtaXhpbiB0aGF0IGFsbG93cyBlbGVtZW50cyB0byB1c2UgdGhlIGA6ZGlyYCBDU1MgU2VsZWN0b3IgdG8gaGF2ZQogICAgICogdGV4dCBkaXJlY3Rpb24gc3BlY2lmaWMgc3R5bGluZy4KICAgICAqCiAgICAgKiBXaXRoIHRoaXMgbWl4aW4sIGFueSBzdHlsZXNoZWV0IHByb3ZpZGVkIGluIHRoZSB0ZW1wbGF0ZSB3aWxsIHRyYW5zZm9ybSBgOmRpcmAgaW50bwogICAgICogYDpob3N0KFtkaXJdKWAgYW5kIHN5bmMgZGlyZWN0aW9uIHdpdGggdGhlIHBhZ2UgdmlhIHRoZSBlbGVtZW50J3MgYGRpcmAgYXR0cmlidXRlLgogICAgICoKICAgICAqIEVsZW1lbnRzIGNhbiBvcHQgb3V0IG9mIHRoZSBnbG9iYWwgcGFnZSB0ZXh0IGRpcmVjdGlvbiBieSBzZXR0aW5nIHRoZSBgZGlyYCBhdHRyaWJ1dGUKICAgICAqIGRpcmVjdGx5IGluIGByZWFkeSgpYCBvciBpbiBIVE1MLgogICAgICoKICAgICAqIENhdmVhdHM6CiAgICAgKiAtIEFwcGxpY2F0aW9ucyBtdXN0IHNldCBgPGh0bWwgZGlyPSJsdHIiPmAgb3IgYDxodG1sIGRpcj0icnRsIj5gIHRvIHN5bmMgZGlyZWN0aW9uCiAgICAgKiAtIEF1dG9tYXRpYyBsZWZ0LXRvLXJpZ2h0IG9yIHJpZ2h0LXRvLWxlZnQgc3R5bGluZyBpcyBzeW5jJ2Qgd2l0aCB0aGUgYDxodG1sPmAgZWxlbWVudCBvbmx5LgogICAgICogLSBDaGFuZ2luZyBgZGlyYCBhdCBydW50aW1lIGlzIHN1cHBvcnRlZC4KICAgICAqIC0gT3B0aW5nIG91dCBvZiB0aGUgZ2xvYmFsIGRpcmVjdGlvbiBzdHlsaW5nIGlzIHBlcm1hbmVudAogICAgICoKICAgICAqIEBtaXhpbkZ1bmN0aW9uCiAgICAgKiBAcG9seW1lcgogICAgICogQGFwcGxpZXNNaXhpbiBQb2x5bWVyLlByb3BlcnR5QWNjZXNzb3JzCiAgICAgKiBAbWVtYmVyb2YgUG9seW1lcgogICAgICovCiAgICBQb2x5bWVyLkRpck1peGluID0gUG9seW1lci5kZWR1cGluZ01peGluKChiYXNlKSA9PiB7CgogICAgICBpZiAoIVNISU1fU0hBRE9XKSB7CiAgICAgICAgaWYgKCFvYnNlcnZlcikgewogICAgICAgICAgZ2V0UlRMKCk7CiAgICAgICAgICBvYnNlcnZlciA9IG5ldyBNdXRhdGlvbk9ic2VydmVyKHVwZGF0ZURpcmVjdGlvbik7CiAgICAgICAgICBvYnNlcnZlci5vYnNlcnZlKGRvY3VtZW50LmRvY3VtZW50RWxlbWVudCwge2F0dHJpYnV0ZXM6IHRydWUsIGF0dHJpYnV0ZUZpbHRlcjogWydkaXInXX0pOwogICAgICAgIH0KICAgICAgfQoKICAgICAgLyoqCiAgICAgICAqIEBjb25zdHJ1Y3RvcgogICAgICAgKiBAZXh0ZW5kcyB7YmFzZX0KICAgICAgICogQGltcGxlbWVudHMge1BvbHltZXJfUHJvcGVydHlBY2Nlc3NvcnN9CiAgICAgICAqIEBwcml2YXRlCiAgICAgICAqLwogICAgICBjb25zdCBlbGVtZW50QmFzZSA9IFBvbHltZXIuUHJvcGVydHlBY2Nlc3NvcnMoYmFzZSk7CgogICAgICAvKioKICAgICAgICogQHBvbHltZXIKICAgICAgICogQG1peGluQ2xhc3MKICAgICAgICogQGltcGxlbWVudHMge1BvbHltZXJfRGlyTWl4aW59CiAgICAgICAqLwogICAgICBjbGFzcyBEaXIgZXh0ZW5kcyBlbGVtZW50QmFzZSB7CgogICAgICAgIC8qKgogICAgICAgICAqIEBvdmVycmlkZQogICAgICAgICAqIEBzdXBwcmVzcyB7bWlzc2luZ1Byb3BlcnRpZXN9IEludGVyZmFjZXMgaW4gY2xvc3VyZSBkbyBub3QgaW5oZXJpdCBzdGF0aWNzLCBidXQgY2xhc3NlcyBkbwogICAgICAgICAqLwogICAgICAgIHN0YXRpYyBfcHJvY2Vzc1N0eWxlVGV4dChjc3NUZXh0LCBiYXNlVVJJKSB7CiAgICAgICAgICBjc3NUZXh0ID0gc3VwZXIuX3Byb2Nlc3NTdHlsZVRleHQoY3NzVGV4dCwgYmFzZVVSSSk7CiAgICAgICAgICBpZiAoIVNISU1fU0hBRE9XICYmIERJUl9DSEVDSy50ZXN0KGNzc1RleHQpKSB7CiAgICAgICAgICAgIGNzc1RleHQgPSB0aGlzLl9yZXBsYWNlRGlySW5Dc3NUZXh0KGNzc1RleHQpOwogICAgICAgICAgICB0aGlzLl9fYWN0aXZhdGVEaXIgPSB0cnVlOwogICAgICAgICAgfQogICAgICAgICAgcmV0dXJuIGNzc1RleHQ7CiAgICAgICAgfQoKICAgICAgICAvKioKICAgICAgICAgKiBSZXBsYWNlIGA6ZGlyYCBpbiB0aGUgZ2l2ZW4gQ1NTIHRleHQKICAgICAgICAgKgogICAgICAgICAqIEBwYXJhbSB7c3RyaW5nfSB0ZXh0IENTUyB0ZXh0IHRvIHJlcGxhY2UgRElSCiAgICAgICAgICogQHJldHVybiB7c3RyaW5nfSBNb2RpZmllZCBDU1MKICAgICAgICAgKi8KICAgICAgICBzdGF0aWMgX3JlcGxhY2VEaXJJbkNzc1RleHQodGV4dCkgewogICAgICAgICAgbGV0IHJlcGxhY2VkVGV4dCA9IHRleHQ7CiAgICAgICAgICByZXBsYWNlZFRleHQgPSByZXBsYWNlZFRleHQucmVwbGFjZShIT1NUX0RJUiwgSE9TVF9ESVJfUkVQTEFDTUVOVCk7CiAgICAgICAgICByZXBsYWNlZFRleHQgPSByZXBsYWNlZFRleHQucmVwbGFjZShFTF9ESVIsIEVMX0RJUl9SRVBMQUNNRU5UKTsKICAgICAgICAgIHJldHVybiByZXBsYWNlZFRleHQ7CiAgICAgICAgfQoKICAgICAgICBjb25zdHJ1Y3RvcigpIHsKICAgICAgICAgIHN1cGVyKCk7CiAgICAgICAgICAvKiogQHR5cGUge2Jvb2xlYW59ICovCiAgICAgICAgICB0aGlzLl9fYXV0b0Rpck9wdE91dCA9IGZhbHNlOwogICAgICAgIH0KCiAgICAgICAgLyoqCiAgICAgICAgICogQHN1cHByZXNzIHtpbnZhbGlkQ2FzdHN9IENsb3N1cmUgZG9lc24ndCB1bmRlcnN0YW5kIHRoYXQgYHRoaXNgIGlzIGFuIEhUTUxFbGVtZW50CiAgICAgICAgICogQHJldHVybiB7dm9pZH0KICAgICAgICAgKi8KICAgICAgICByZWFkeSgpIHsKICAgICAgICAgIHN1cGVyLnJlYWR5KCk7CiAgICAgICAgICB0aGlzLl9fYXV0b0Rpck9wdE91dCA9IC8qKiBAdHlwZSB7IUhUTUxFbGVtZW50fSAqLyh0aGlzKS5oYXNBdHRyaWJ1dGUoJ2RpcicpOwogICAgICAgIH0KCiAgICAgICAgLyoqCiAgICAgICAgICogQHN1cHByZXNzIHttaXNzaW5nUHJvcGVydGllc30gSWYgaXQgZXhpc3RzIG9uIGVsZW1lbnRCYXNlLCBpdCBjYW4gYmUgc3VwZXInZAogICAgICAgICAqIEByZXR1cm4ge3ZvaWR9CiAgICAgICAgICovCiAgICAgICAgY29ubmVjdGVkQ2FsbGJhY2soKSB7CiAgICAgICAgICBpZiAoZWxlbWVudEJhc2UucHJvdG90eXBlLmNvbm5lY3RlZENhbGxiYWNrKSB7CiAgICAgICAgICAgIHN1cGVyLmNvbm5lY3RlZENhbGxiYWNrKCk7CiAgICAgICAgICB9CiAgICAgICAgICBpZiAodGhpcy5jb25zdHJ1Y3Rvci5fX2FjdGl2YXRlRGlyKSB7CiAgICAgICAgICAgIHRha2VSZWNvcmRzKCk7CiAgICAgICAgICAgIERJUl9JTlNUQU5DRVMucHVzaCh0aGlzKTsKICAgICAgICAgICAgc2V0UlRMKHRoaXMpOwogICAgICAgICAgfQogICAgICAgIH0KCiAgICAgICAgLyoqCiAgICAgICAgICogQHN1cHByZXNzIHttaXNzaW5nUHJvcGVydGllc30gSWYgaXQgZXhpc3RzIG9uIGVsZW1lbnRCYXNlLCBpdCBjYW4gYmUgc3VwZXInZAogICAgICAgICAqIEByZXR1cm4ge3ZvaWR9CiAgICAgICAgICovCiAgICAgICAgZGlzY29ubmVjdGVkQ2FsbGJhY2soKSB7CiAgICAgICAgICBpZiAoZWxlbWVudEJhc2UucHJvdG90eXBlLmRpc2Nvbm5lY3RlZENhbGxiYWNrKSB7CiAgICAgICAgICAgIHN1cGVyLmRpc2Nvbm5lY3RlZENhbGxiYWNrKCk7CiAgICAgICAgICB9CiAgICAgICAgICBpZiAodGhpcy5jb25zdHJ1Y3Rvci5fX2FjdGl2YXRlRGlyKSB7CiAgICAgICAgICAgIGNvbnN0IGlkeCA9IERJUl9JTlNUQU5DRVMuaW5kZXhPZih0aGlzKTsKICAgICAgICAgICAgaWYgKGlkeCA+IC0xKSB7CiAgICAgICAgICAgICAgRElSX0lOU1RBTkNFUy5zcGxpY2UoaWR4LCAxKTsKICAgICAgICAgICAgfQogICAgICAgICAgfQogICAgICAgIH0KICAgICAgfQoKICAgICAgRGlyLl9fYWN0aXZhdGVEaXIgPSBmYWxzZTsKCiAgICAgIHJldHVybiBEaXI7CiAgICB9KTsKICB9KSgpOwo8L3NjcmlwdD48c2NyaXB0PgoKKGZ1bmN0aW9uKCkgewoKICAndXNlIHN0cmljdCc7CgogIC8vIHJ1biBhIGNhbGxiYWNrIHdoZW4gSFRNTEltcG9ydHMgYXJlIHJlYWR5IG9yIGltbWVkaWF0ZWx5IGlmCiAgLy8gdGhpcyBhcGkgaXMgbm90IGF2YWlsYWJsZS4KICBmdW5jdGlvbiB3aGVuSW1wb3J0c1JlYWR5KGNiKSB7CiAgICBpZiAod2luZG93LkhUTUxJbXBvcnRzKSB7CiAgICAgIEhUTUxJbXBvcnRzLndoZW5SZWFkeShjYik7CiAgICB9IGVsc2UgewogICAgICBjYigpOwogICAgfQogIH0KCiAgLyoqCiAgICogQ29udmVuaWVuY2UgbWV0aG9kIGZvciBpbXBvcnRpbmcgYW4gSFRNTCBkb2N1bWVudCBpbXBlcmF0aXZlbHkuCiAgICoKICAgKiBUaGlzIG1ldGhvZCBjcmVhdGVzIGEgbmV3IGA8bGluayByZWw9ImltcG9ydCI+YCBlbGVtZW50IHdpdGgKICAgKiB0aGUgcHJvdmlkZWQgVVJMIGFuZCBhcHBlbmRzIGl0IHRvIHRoZSBkb2N1bWVudCB0byBzdGFydCBsb2FkaW5nLgogICAqIEluIHRoZSBgb25sb2FkYCBjYWxsYmFjaywgdGhlIGBpbXBvcnRgIHByb3BlcnR5IG9mIHRoZSBgbGlua2AKICAgKiBlbGVtZW50IHdpbGwgY29udGFpbiB0aGUgaW1wb3J0ZWQgZG9jdW1lbnQgY29udGVudHMuCiAgICoKICAgKiBAbWVtYmVyb2YgUG9seW1lcgogICAqIEBwYXJhbSB7c3RyaW5nfSBocmVmIFVSTCB0byBkb2N1bWVudCB0byBsb2FkLgogICAqIEBwYXJhbSB7P2Z1bmN0aW9uKCFFdmVudCk6dm9pZD19IG9ubG9hZCBDYWxsYmFjayB0byBub3RpZnkgd2hlbiBhbiBpbXBvcnQgc3VjY2Vzc2Z1bGx5CiAgICogICBsb2FkZWQuCiAgICogQHBhcmFtIHs/ZnVuY3Rpb24oIUVycm9y |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment