Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save firmai/27c9042a4568d6ddc3cb4802659df0dd to your computer and use it in GitHub Desktop.
Save firmai/27c9042a4568d6ddc3cb4802659df0dd to your computer and use it in GitHub Desktop.
MLFin Replicate Paper by Rapach, Strauss, Tu, Zhou.ipynb
Display the source blob
Display the rendered blob
Raw
{
"nbformat": 4,
"nbformat_minor": 0,
"metadata": {
"colab": {
"name": "MLFin Replicate Paper by Rapach, Strauss, Tu, Zhou.ipynb",
"provenance": [],
"collapsed_sections": [
"GKFBQzIHfkbr"
],
"include_colab_link": true
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.6.6"
},
"kernelspec": {
"display_name": "tensorflow3",
"language": "python",
"name": "tensorflow3"
},
"accelerator": "GPU"
},
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "view-in-github",
"colab_type": "text"
},
"source": [
"<a href=\"https://colab.research.google.com/gist/firmai/27c9042a4568d6ddc3cb4802659df0dd/mlfin-replicate-paper-by-rapach-strauss-tu-zhou.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "aIP2O7ORjARs"
},
"source": [
"## Industury Factors"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "fQKJm2jGEfZF"
},
"source": [
"\n",
"This notebook is wholly inspired by Druce Vertes from Streeteye.com, do pay him some support."
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "3lRSi-4zfkXp"
},
"source": [
"Replicate [Dynamic Return Dependencies Across Industries: A Machine Learning Approach](https://papers.ssrn.com/sol3/papers.cfm?abstract_id=3120110&download=yes) by David Rapach, Jack Strauss, Jun Tu and Guofu Zhou.\n",
"\n",
"1) Use industry returns from [Ken French](http://mba.tuck.dartmouth.edu/pages/faculty/ken.french/data_library.html)\n",
"\n",
"2) Forecast (for example) this month's Chemical industry return using last month's returns from all 30 industries\n",
"\n",
"3) Use LASSO for predictor subset selection over the entire 1960-2016 period to determine that e.g. Beer is predicted by Food, Clothing, Coal\n",
"\n",
"4) Use LASSO-selected predictors and simple linear regression to predict returns\n",
"\n",
"5) Generate portfolios and run backtests.\n",
"\n",
"- Predictor selection - finds same predictors except 2 industries. Possibly use of AICc instead of AIC (don't see an sklearn implementation that uses AICc)\n",
"\n",
"- Prediction by industry - R-squareds line up pretty closely\n",
"\n",
"- Portfolio performance, similar ballpark results. Maybe AICc/AIC; Also paper standardizes predictors, which is pretty standard. Finally, for some reason their mean returns don't line up to geometric mean annualized, they seem to be calculating something different.\n",
"\n",
"- Replicating exactly is hard but it does replicate closely and perform well\n",
"\n",
"6) Run various sklearn regressors to see which performs best, understand metrics that predict performance. MSE does not predict Sharpe. Kendall's tau, i.e. correlation of predicted vs. actual rankings, performs better.\n",
"\n",
"7) Tune ElasticNet to get slightly better performance than Lasso/OLS\n",
"\n",
"8) Run Keras NNs. They don't improve on Lasso/OLS or ElasticNet.\n",
"\n",
"\n",
""
]
},
{
"cell_type": "code",
"metadata": {
"id": "8NNyTvkGkA5U"
},
"source": [
"#!pip install beakerx"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "7V4P7roTkUTP",
"colab": {
"base_uri": "https://localhost:8080/"
},
"outputId": "2202bcaf-65aa-46e9-a662-3825d65a220c"
},
"source": [
"# !pip install -U scikit-learn==0.21.3\n",
"!pip install plotly==2.0.0\n",
"# restart when done"
],
"execution_count": null,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Requirement already satisfied: plotly==2.0.0 in /usr/local/lib/python3.7/dist-packages (2.0.0)\n",
"Requirement already satisfied: six in /usr/local/lib/python3.7/dist-packages (from plotly==2.0.0) (1.15.0)\n",
"Requirement already satisfied: requests in /usr/local/lib/python3.7/dist-packages (from plotly==2.0.0) (2.23.0)\n",
"Requirement already satisfied: pytz in /usr/local/lib/python3.7/dist-packages (from plotly==2.0.0) (2018.9)\n",
"Requirement already satisfied: urllib3!=1.25.0,!=1.25.1,<1.26,>=1.21.1 in /usr/local/lib/python3.7/dist-packages (from requests->plotly==2.0.0) (1.24.3)\n",
"Requirement already satisfied: chardet<4,>=3.0.2 in /usr/local/lib/python3.7/dist-packages (from requests->plotly==2.0.0) (3.0.4)\n",
"Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.7/dist-packages (from requests->plotly==2.0.0) (2021.10.8)\n",
"Requirement already satisfied: idna<3,>=2.5 in /usr/local/lib/python3.7/dist-packages (from requests->plotly==2.0.0) (2.10)\n"
]
}
]
},
{
"cell_type": "code",
"metadata": {
"id": "-xj-ey0VfkXq",
"outputId": "0a1f835c-3327-4218-fef5-b21bfb53f3f1",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 141
}
},
"source": [
"import os\n",
"import sys\n",
"import warnings\n",
"import numpy as np\n",
"import pandas as pd\n",
"import time\n",
"import copy\n",
"import random\n",
"from itertools import product\n",
"\n",
"os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' #Hide messy TensorFlow warnings\n",
"warnings.filterwarnings(\"ignore\") #Hide messy numpy warnings\n",
"\n",
"import sklearn\n",
"from sklearn.model_selection import train_test_split, KFold\n",
"from sklearn.metrics import confusion_matrix, mean_squared_error, explained_variance_score, r2_score, accuracy_score\n",
"from sklearn.linear_model import LinearRegression, Lasso, lasso_path, lars_path, LassoLarsIC\n",
"from sklearn.ensemble._forest import RandomForestRegressor\n",
"from sklearn.neural_network import MLPRegressor\n",
"from sklearn.preprocessing import MinMaxScaler, StandardScaler\n",
"from sklearn.utils import all_estimators\n",
"import xgboost\n",
"\n",
"from scipy.stats import chisquare, kendalltau\n",
"\n",
"import tensorflow.compat.v1 as tf\n",
"tf.disable_v2_behavior()\n",
"\n",
"tf.set_random_seed(1764)\n",
"# tf.random.set_seed()\n",
"print(tf.__version__)\n",
"# confirm GPU is in use\n",
"with tf.device('/gpu:0'):\n",
" a = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[2, 3], name='a')\n",
" b = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[3, 2], name='b')\n",
" c = tf.matmul(a, b)\n",
"\n",
"with tf.Session() as sess:\n",
" print (sess.run(c))\n",
"\n",
"import keras\n",
"from keras.layers.core import Dense, Activation\n",
"from keras.layers import Input\n",
"from keras.models import Model\n",
"\n",
"from keras.layers.recurrent import LSTM, GRU\n",
"from keras.regularizers import l1\n",
"from keras.models import Sequential\n",
"from keras.models import load_model\n",
"\n",
"#import ffn\n",
"import matplotlib.pyplot as plt\n",
"%matplotlib inline\n",
"import seaborn as sns\n",
"\n",
"# this will make dataframe display as sortable widget\n",
"# commented out because sortable tables not viewable using nbviewer\n",
"#from beakerx import *\n",
"\n",
"import plotly\n",
"# print (plotly.__version__) # requires version >= 1.9.0\n",
"from plotly.offline import download_plotlyjs, init_notebook_mode, plot, iplot\n",
"from plotly.graph_objs import *\n",
"import plotly.figure_factory as ff\n",
"\n",
"init_notebook_mode(connected=True)\n",
"\n",
"random.seed(1764)\n",
"np.random.seed(1764)\n"
],
"execution_count": null,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"WARNING:tensorflow:From /usr/local/lib/python3.7/dist-packages/tensorflow/python/compat/v2_compat.py:111: disable_resource_variables (from tensorflow.python.ops.variable_scope) is deprecated and will be removed in a future version.\n",
"Instructions for updating:\n",
"non-resource variables are not supported in the long term\n",
"2.7.0\n",
"[[22. 28.]\n",
" [49. 64.]]\n"
]
},
{
"output_type": "display_data",
"data": {
"text/html": [
"<script>requirejs.config({paths: { 'plotly': ['https://cdn.plot.ly/plotly-latest.min']},});if(!window.Plotly) {{require(['plotly'],function(plotly) {window.Plotly=plotly;});}}</script>"
],
"text/plain": [
"<IPython.core.display.HTML object>"
]
},
"metadata": {}
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "M7dE96IAfkXy"
},
"source": [
"### 1. Replicate paper"
]
},
{
"cell_type": "code",
"metadata": {
"id": "f3DSQj07mM8d"
},
"source": [
"## Save future files to your drive\n",
"import numpy as np\n",
"# from google.colab import drive\n",
"# drive.mount('/content/drive',force_remount=True)\n",
"# %cd \"/content/drive/My Drive/FirmAI/FinML/Data/Industry Factors\"\n",
"\n"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "dyjHTej4fkXz",
"outputId": "598cdfab-5b0a-4fa2-e6a9-92a16b617473",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 490
}
},
"source": [
"print(\"Loading data...\")\n",
"data = pd.read_csv(\"https://storage.googleapis.com/public-quant/course//content/30_Industry_Portfolios.csv\")\n",
"data = data.set_index('yyyymm')\n",
"industries = list(data.columns)\n",
"# map industry names to col nums\n",
"ind_reverse_dict = dict([(industries[i], i) for i in range(len(industries))])\n",
"\n",
"rfdata = pd.read_csv(\"https://storage.googleapis.com/public-quant/course//content/F-F_Research_Data_Factors.csv\")\n",
"rfdata = rfdata.set_index('yyyymm')\n",
"data['rf'] = rfdata['RF']\n",
"\n",
"# subtract risk-free rate\n",
"# create a response variable led by 1 period to predict\n",
"for ind in industries:\n",
" data[ind] = data[ind] - data['rf']\n",
"\n",
"#for ind in industries:\n",
"# data[ind+\".3m\"] = pd.rolling_mean(data[ind],3)\n",
"\n",
"#for ind in industries:\n",
"# data[ind+\".6m\"] = pd.rolling_mean(data[ind],6)\n",
"\n",
"#for ind in industries:\n",
"# data[ind+\".12m\"] = pd.rolling_mean(data[ind],12)\n",
"\n",
"# Create lead columns\n",
"for ind in industries:\n",
" data[ind+\".lead\"] = data[ind].shift(-1)\n",
"\n",
"data = data.loc[data.index[data.index > 195911]]\n",
"data = data.drop(columns=['rf'])\n",
"data = data.dropna(axis=0, how='any')\n",
"\n",
"nresponses = len(industries)\n",
"npredictors = data.shape[1]-nresponses\n",
"\n",
"predictors = list(data.columns[:npredictors])\n",
"predictor_reverse_dict = dict([(predictors[i], i) for i in range(len(predictors))])\n",
"\n",
"responses = list(data.columns[-nresponses:])\n",
"response_reverse_dict = dict([(responses[i], i) for i in range(len(responses))])\n",
"\n",
"print(data.shape)\n",
"\n",
"data[['Food', 'Food.lead']]\n"
],
"execution_count": null,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Loading data...\n",
"(697, 60)\n"
]
},
{
"output_type": "execute_result",
"data": {
"text/html": [
"\n",
" <div id=\"df-b5ad4dff-2108-46e2-8618-a628d53c0497\">\n",
" <div class=\"colab-df-container\">\n",
" <div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>Food</th>\n",
" <th>Food.lead</th>\n",
" </tr>\n",
" <tr>\n",
" <th>yyyymm</th>\n",
" <th></th>\n",
" <th></th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>195912</th>\n",
" <td>2.01</td>\n",
" <td>-4.49</td>\n",
" </tr>\n",
" <tr>\n",
" <th>196001</th>\n",
" <td>-4.49</td>\n",
" <td>3.35</td>\n",
" </tr>\n",
" <tr>\n",
" <th>196002</th>\n",
" <td>3.35</td>\n",
" <td>-1.67</td>\n",
" </tr>\n",
" <tr>\n",
" <th>196003</th>\n",
" <td>-1.67</td>\n",
" <td>1.17</td>\n",
" </tr>\n",
" <tr>\n",
" <th>196004</th>\n",
" <td>1.17</td>\n",
" <td>8.20</td>\n",
" </tr>\n",
" <tr>\n",
" <th>...</th>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" </tr>\n",
" <tr>\n",
" <th>201708</th>\n",
" <td>-2.77</td>\n",
" <td>0.43</td>\n",
" </tr>\n",
" <tr>\n",
" <th>201709</th>\n",
" <td>0.43</td>\n",
" <td>0.71</td>\n",
" </tr>\n",
" <tr>\n",
" <th>201710</th>\n",
" <td>0.71</td>\n",
" <td>4.15</td>\n",
" </tr>\n",
" <tr>\n",
" <th>201711</th>\n",
" <td>4.15</td>\n",
" <td>-0.10</td>\n",
" </tr>\n",
" <tr>\n",
" <th>201712</th>\n",
" <td>-0.10</td>\n",
" <td>2.27</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"<p>697 rows × 2 columns</p>\n",
"</div>\n",
" <button class=\"colab-df-convert\" onclick=\"convertToInteractive('df-b5ad4dff-2108-46e2-8618-a628d53c0497')\"\n",
" title=\"Convert this dataframe to an interactive table.\"\n",
" style=\"display:none;\">\n",
" \n",
" <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\"viewBox=\"0 0 24 24\"\n",
" width=\"24px\">\n",
" <path d=\"M0 0h24v24H0V0z\" fill=\"none\"/>\n",
" <path d=\"M18.56 5.44l.94 2.06.94-2.06 2.06-.94-2.06-.94-.94-2.06-.94 2.06-2.06.94zm-11 1L8.5 8.5l.94-2.06 2.06-.94-2.06-.94L8.5 2.5l-.94 2.06-2.06.94zm10 10l.94 2.06.94-2.06 2.06-.94-2.06-.94-.94-2.06-.94 2.06-2.06.94z\"/><path d=\"M17.41 7.96l-1.37-1.37c-.4-.4-.92-.59-1.43-.59-.52 0-1.04.2-1.43.59L10.3 9.45l-7.72 7.72c-.78.78-.78 2.05 0 2.83L4 21.41c.39.39.9.59 1.41.59.51 0 1.02-.2 1.41-.59l7.78-7.78 2.81-2.81c.8-.78.8-2.07 0-2.86zM5.41 20L4 18.59l7.72-7.72 1.47 1.35L5.41 20z\"/>\n",
" </svg>\n",
" </button>\n",
" \n",
" <style>\n",
" .colab-df-container {\n",
" display:flex;\n",
" flex-wrap:wrap;\n",
" gap: 12px;\n",
" }\n",
"\n",
" .colab-df-convert {\n",
" background-color: #E8F0FE;\n",
" border: none;\n",
" border-radius: 50%;\n",
" cursor: pointer;\n",
" display: none;\n",
" fill: #1967D2;\n",
" height: 32px;\n",
" padding: 0 0 0 0;\n",
" width: 32px;\n",
" }\n",
"\n",
" .colab-df-convert:hover {\n",
" background-color: #E2EBFA;\n",
" box-shadow: 0px 1px 2px rgba(60, 64, 67, 0.3), 0px 1px 3px 1px rgba(60, 64, 67, 0.15);\n",
" fill: #174EA6;\n",
" }\n",
"\n",
" [theme=dark] .colab-df-convert {\n",
" background-color: #3B4455;\n",
" fill: #D2E3FC;\n",
" }\n",
"\n",
" [theme=dark] .colab-df-convert:hover {\n",
" background-color: #434B5C;\n",
" box-shadow: 0px 1px 3px 1px rgba(0, 0, 0, 0.15);\n",
" filter: drop-shadow(0px 1px 2px rgba(0, 0, 0, 0.3));\n",
" fill: #FFFFFF;\n",
" }\n",
" </style>\n",
"\n",
" <script>\n",
" const buttonEl =\n",
" document.querySelector('#df-b5ad4dff-2108-46e2-8618-a628d53c0497 button.colab-df-convert');\n",
" buttonEl.style.display =\n",
" google.colab.kernel.accessAllowed ? 'block' : 'none';\n",
"\n",
" async function convertToInteractive(key) {\n",
" const element = document.querySelector('#df-b5ad4dff-2108-46e2-8618-a628d53c0497');\n",
" const dataTable =\n",
" await google.colab.kernel.invokeFunction('convertToInteractive',\n",
" [key], {});\n",
" if (!dataTable) return;\n",
"\n",
" const docLinkHtml = 'Like what you see? Visit the ' +\n",
" '<a target=\"_blank\" href=https://colab.research.google.com/notebooks/data_table.ipynb>data table notebook</a>'\n",
" + ' to learn more about interactive tables.';\n",
" element.innerHTML = '';\n",
" dataTable['output_type'] = 'display_data';\n",
" await google.colab.output.renderOutput(dataTable, element);\n",
" const docLink = document.createElement('div');\n",
" docLink.innerHTML = docLinkHtml;\n",
" element.appendChild(docLink);\n",
" }\n",
" </script>\n",
" </div>\n",
" </div>\n",
" "
],
"text/plain": [
" Food Food.lead\n",
"yyyymm \n",
"195912 2.01 -4.49\n",
"196001 -4.49 3.35\n",
"196002 3.35 -1.67\n",
"196003 -1.67 1.17\n",
"196004 1.17 8.20\n",
"... ... ...\n",
"201708 -2.77 0.43\n",
"201709 0.43 0.71\n",
"201710 0.71 4.15\n",
"201711 4.15 -0.10\n",
"201712 -0.10 2.27\n",
"\n",
"[697 rows x 2 columns]"
]
},
"metadata": {},
"execution_count": 4
}
]
},
{
"cell_type": "code",
"metadata": {
"id": "CY8mnEjbfkX3",
"outputId": "b3611434-03e8-4f42-d28b-eea23407bf00",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 519
}
},
"source": [
"# exclude 2017 and later to tie to paper\n",
"data = data.loc[data.index[data.index < 201701]]\n",
"data = data.loc[data.index[data.index > 195911]]\n",
"data\n"
],
"execution_count": null,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/html": [
"\n",
" <div id=\"df-f5d9f629-80aa-450d-9077-34374d1aa85f\">\n",
" <div class=\"colab-df-container\">\n",
" <div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>Food</th>\n",
" <th>Beer</th>\n",
" <th>Smoke</th>\n",
" <th>Games</th>\n",
" <th>Books</th>\n",
" <th>Hshld</th>\n",
" <th>Clths</th>\n",
" <th>Hlth</th>\n",
" <th>Chems</th>\n",
" <th>Txtls</th>\n",
" <th>Cnstr</th>\n",
" <th>Steel</th>\n",
" <th>FabPr</th>\n",
" <th>ElcEq</th>\n",
" <th>Autos</th>\n",
" <th>Carry</th>\n",
" <th>Mines</th>\n",
" <th>Coal</th>\n",
" <th>Oil</th>\n",
" <th>Util</th>\n",
" <th>Telcm</th>\n",
" <th>Servs</th>\n",
" <th>BusEq</th>\n",
" <th>Paper</th>\n",
" <th>Trans</th>\n",
" <th>Whlsl</th>\n",
" <th>Rtail</th>\n",
" <th>Meals</th>\n",
" <th>Fin</th>\n",
" <th>Other</th>\n",
" <th>Food.lead</th>\n",
" <th>Beer.lead</th>\n",
" <th>Smoke.lead</th>\n",
" <th>Games.lead</th>\n",
" <th>Books.lead</th>\n",
" <th>Hshld.lead</th>\n",
" <th>Clths.lead</th>\n",
" <th>Hlth.lead</th>\n",
" <th>Chems.lead</th>\n",
" <th>Txtls.lead</th>\n",
" <th>Cnstr.lead</th>\n",
" <th>Steel.lead</th>\n",
" <th>FabPr.lead</th>\n",
" <th>ElcEq.lead</th>\n",
" <th>Autos.lead</th>\n",
" <th>Carry.lead</th>\n",
" <th>Mines.lead</th>\n",
" <th>Coal.lead</th>\n",
" <th>Oil.lead</th>\n",
" <th>Util.lead</th>\n",
" <th>Telcm.lead</th>\n",
" <th>Servs.lead</th>\n",
" <th>BusEq.lead</th>\n",
" <th>Paper.lead</th>\n",
" <th>Trans.lead</th>\n",
" <th>Whlsl.lead</th>\n",
" <th>Rtail.lead</th>\n",
" <th>Meals.lead</th>\n",
" <th>Fin.lead</th>\n",
" <th>Other.lead</th>\n",
" </tr>\n",
" <tr>\n",
" <th>yyyymm</th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>195912</th>\n",
" <td>2.01</td>\n",
" <td>0.35</td>\n",
" <td>-3.02</td>\n",
" <td>1.64</td>\n",
" <td>7.29</td>\n",
" <td>0.67</td>\n",
" <td>1.87</td>\n",
" <td>-1.97</td>\n",
" <td>3.08</td>\n",
" <td>0.74</td>\n",
" <td>0.72</td>\n",
" <td>2.18</td>\n",
" <td>3.11</td>\n",
" <td>5.53</td>\n",
" <td>5.28</td>\n",
" <td>-4.88</td>\n",
" <td>4.40</td>\n",
" <td>2.07</td>\n",
" <td>3.93</td>\n",
" <td>1.79</td>\n",
" <td>3.97</td>\n",
" <td>-1.96</td>\n",
" <td>0.67</td>\n",
" <td>1.46</td>\n",
" <td>2.03</td>\n",
" <td>-0.30</td>\n",
" <td>2.41</td>\n",
" <td>2.71</td>\n",
" <td>0.15</td>\n",
" <td>-6.50</td>\n",
" <td>-4.49</td>\n",
" <td>-5.71</td>\n",
" <td>-2.05</td>\n",
" <td>1.21</td>\n",
" <td>-5.47</td>\n",
" <td>-7.84</td>\n",
" <td>-8.53</td>\n",
" <td>-6.68</td>\n",
" <td>-10.03</td>\n",
" <td>-4.77</td>\n",
" <td>-6.67</td>\n",
" <td>-9.38</td>\n",
" <td>-4.42</td>\n",
" <td>-12.30</td>\n",
" <td>-11.71</td>\n",
" <td>-5.03</td>\n",
" <td>-3.81</td>\n",
" <td>-7.91</td>\n",
" <td>-7.82</td>\n",
" <td>-2.40</td>\n",
" <td>0.62</td>\n",
" <td>-6.18</td>\n",
" <td>-7.93</td>\n",
" <td>-9.41</td>\n",
" <td>-4.31</td>\n",
" <td>-5.33</td>\n",
" <td>-6.09</td>\n",
" <td>-10.08</td>\n",
" <td>-4.68</td>\n",
" <td>-3.98</td>\n",
" </tr>\n",
" <tr>\n",
" <th>196001</th>\n",
" <td>-4.49</td>\n",
" <td>-5.71</td>\n",
" <td>-2.05</td>\n",
" <td>1.21</td>\n",
" <td>-5.47</td>\n",
" <td>-7.84</td>\n",
" <td>-8.53</td>\n",
" <td>-6.68</td>\n",
" <td>-10.03</td>\n",
" <td>-4.77</td>\n",
" <td>-6.67</td>\n",
" <td>-9.38</td>\n",
" <td>-4.42</td>\n",
" <td>-12.30</td>\n",
" <td>-11.71</td>\n",
" <td>-5.03</td>\n",
" <td>-3.81</td>\n",
" <td>-7.91</td>\n",
" <td>-7.82</td>\n",
" <td>-2.40</td>\n",
" <td>0.62</td>\n",
" <td>-6.18</td>\n",
" <td>-7.93</td>\n",
" <td>-9.41</td>\n",
" <td>-4.31</td>\n",
" <td>-5.33</td>\n",
" <td>-6.09</td>\n",
" <td>-10.08</td>\n",
" <td>-4.68</td>\n",
" <td>-3.98</td>\n",
" <td>3.35</td>\n",
" <td>-2.14</td>\n",
" <td>2.27</td>\n",
" <td>4.23</td>\n",
" <td>2.39</td>\n",
" <td>9.31</td>\n",
" <td>1.44</td>\n",
" <td>-0.02</td>\n",
" <td>-0.74</td>\n",
" <td>0.32</td>\n",
" <td>3.07</td>\n",
" <td>-2.03</td>\n",
" <td>-2.13</td>\n",
" <td>5.11</td>\n",
" <td>-2.15</td>\n",
" <td>2.05</td>\n",
" <td>1.57</td>\n",
" <td>-2.97</td>\n",
" <td>-3.74</td>\n",
" <td>2.48</td>\n",
" <td>8.07</td>\n",
" <td>9.13</td>\n",
" <td>5.09</td>\n",
" <td>3.00</td>\n",
" <td>-0.94</td>\n",
" <td>1.42</td>\n",
" <td>4.00</td>\n",
" <td>1.81</td>\n",
" <td>-0.98</td>\n",
" <td>6.32</td>\n",
" </tr>\n",
" <tr>\n",
" <th>196002</th>\n",
" <td>3.35</td>\n",
" <td>-2.14</td>\n",
" <td>2.27</td>\n",
" <td>4.23</td>\n",
" <td>2.39</td>\n",
" <td>9.31</td>\n",
" <td>1.44</td>\n",
" <td>-0.02</td>\n",
" <td>-0.74</td>\n",
" <td>0.32</td>\n",
" <td>3.07</td>\n",
" <td>-2.03</td>\n",
" <td>-2.13</td>\n",
" <td>5.11</td>\n",
" <td>-2.15</td>\n",
" <td>2.05</td>\n",
" <td>1.57</td>\n",
" <td>-2.97</td>\n",
" <td>-3.74</td>\n",
" <td>2.48</td>\n",
" <td>8.07</td>\n",
" <td>9.13</td>\n",
" <td>5.09</td>\n",
" <td>3.00</td>\n",
" <td>-0.94</td>\n",
" <td>1.42</td>\n",
" <td>4.00</td>\n",
" <td>1.81</td>\n",
" <td>-0.98</td>\n",
" <td>6.32</td>\n",
" <td>-1.67</td>\n",
" <td>-2.94</td>\n",
" <td>-0.18</td>\n",
" <td>-0.65</td>\n",
" <td>2.18</td>\n",
" <td>-0.56</td>\n",
" <td>-2.59</td>\n",
" <td>1.26</td>\n",
" <td>-2.75</td>\n",
" <td>-6.79</td>\n",
" <td>-0.78</td>\n",
" <td>-5.58</td>\n",
" <td>-3.58</td>\n",
" <td>-0.89</td>\n",
" <td>-6.64</td>\n",
" <td>-8.19</td>\n",
" <td>-2.99</td>\n",
" <td>-5.47</td>\n",
" <td>-1.07</td>\n",
" <td>1.42</td>\n",
" <td>-0.21</td>\n",
" <td>-0.31</td>\n",
" <td>3.34</td>\n",
" <td>-2.43</td>\n",
" <td>-4.99</td>\n",
" <td>-1.37</td>\n",
" <td>-0.13</td>\n",
" <td>-3.88</td>\n",
" <td>0.05</td>\n",
" <td>-2.43</td>\n",
" </tr>\n",
" <tr>\n",
" <th>196003</th>\n",
" <td>-1.67</td>\n",
" <td>-2.94</td>\n",
" <td>-0.18</td>\n",
" <td>-0.65</td>\n",
" <td>2.18</td>\n",
" <td>-0.56</td>\n",
" <td>-2.59</td>\n",
" <td>1.26</td>\n",
" <td>-2.75</td>\n",
" <td>-6.79</td>\n",
" <td>-0.78</td>\n",
" <td>-5.58</td>\n",
" <td>-3.58</td>\n",
" <td>-0.89</td>\n",
" <td>-6.64</td>\n",
" <td>-8.19</td>\n",
" <td>-2.99</td>\n",
" <td>-5.47</td>\n",
" <td>-1.07</td>\n",
" <td>1.42</td>\n",
" <td>-0.21</td>\n",
" <td>-0.31</td>\n",
" <td>3.34</td>\n",
" <td>-2.43</td>\n",
" <td>-4.99</td>\n",
" <td>-1.37</td>\n",
" <td>-0.13</td>\n",
" <td>-3.88</td>\n",
" <td>0.05</td>\n",
" <td>-2.43</td>\n",
" <td>1.17</td>\n",
" <td>-2.16</td>\n",
" <td>1.35</td>\n",
" <td>6.46</td>\n",
" <td>-1.17</td>\n",
" <td>-1.27</td>\n",
" <td>0.21</td>\n",
" <td>1.49</td>\n",
" <td>-5.53</td>\n",
" <td>-1.10</td>\n",
" <td>-1.45</td>\n",
" <td>-5.53</td>\n",
" <td>-4.10</td>\n",
" <td>-1.11</td>\n",
" <td>-2.31</td>\n",
" <td>-1.36</td>\n",
" <td>-0.81</td>\n",
" <td>-2.97</td>\n",
" <td>-4.48</td>\n",
" <td>1.18</td>\n",
" <td>-1.24</td>\n",
" <td>7.14</td>\n",
" <td>1.77</td>\n",
" <td>0.41</td>\n",
" <td>-2.13</td>\n",
" <td>0.45</td>\n",
" <td>-0.53</td>\n",
" <td>8.86</td>\n",
" <td>-0.64</td>\n",
" <td>0.55</td>\n",
" </tr>\n",
" <tr>\n",
" <th>196004</th>\n",
" <td>1.17</td>\n",
" <td>-2.16</td>\n",
" <td>1.35</td>\n",
" <td>6.46</td>\n",
" <td>-1.17</td>\n",
" <td>-1.27</td>\n",
" <td>0.21</td>\n",
" <td>1.49</td>\n",
" <td>-5.53</td>\n",
" <td>-1.10</td>\n",
" <td>-1.45</td>\n",
" <td>-5.53</td>\n",
" <td>-4.10</td>\n",
" <td>-1.11</td>\n",
" <td>-2.31</td>\n",
" <td>-1.36</td>\n",
" <td>-0.81</td>\n",
" <td>-2.97</td>\n",
" <td>-4.48</td>\n",
" <td>1.18</td>\n",
" <td>-1.24</td>\n",
" <td>7.14</td>\n",
" <td>1.77</td>\n",
" <td>0.41</td>\n",
" <td>-2.13</td>\n",
" <td>0.45</td>\n",
" <td>-0.53</td>\n",
" <td>8.86</td>\n",
" <td>-0.64</td>\n",
" <td>0.55</td>\n",
" <td>8.20</td>\n",
" <td>-0.52</td>\n",
" <td>2.44</td>\n",
" <td>7.28</td>\n",
" <td>11.67</td>\n",
" <td>7.74</td>\n",
" <td>1.74</td>\n",
" <td>13.50</td>\n",
" <td>3.40</td>\n",
" <td>2.10</td>\n",
" <td>3.18</td>\n",
" <td>2.35</td>\n",
" <td>0.92</td>\n",
" <td>6.48</td>\n",
" <td>0.27</td>\n",
" <td>9.27</td>\n",
" <td>-1.39</td>\n",
" <td>2.41</td>\n",
" <td>-3.85</td>\n",
" <td>1.25</td>\n",
" <td>3.05</td>\n",
" <td>-1.75</td>\n",
" <td>11.90</td>\n",
" <td>2.85</td>\n",
" <td>0.90</td>\n",
" <td>1.65</td>\n",
" <td>3.11</td>\n",
" <td>0.80</td>\n",
" <td>-0.45</td>\n",
" <td>1.02</td>\n",
" </tr>\n",
" <tr>\n",
" <th>...</th>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" </tr>\n",
" <tr>\n",
" <th>201608</th>\n",
" <td>-0.52</td>\n",
" <td>-0.90</td>\n",
" <td>-1.22</td>\n",
" <td>0.94</td>\n",
" <td>0.29</td>\n",
" <td>1.24</td>\n",
" <td>1.37</td>\n",
" <td>-3.24</td>\n",
" <td>2.48</td>\n",
" <td>1.03</td>\n",
" <td>0.80</td>\n",
" <td>-7.10</td>\n",
" <td>3.83</td>\n",
" <td>1.34</td>\n",
" <td>-0.32</td>\n",
" <td>0.29</td>\n",
" <td>-9.79</td>\n",
" <td>-4.54</td>\n",
" <td>1.39</td>\n",
" <td>-3.94</td>\n",
" <td>-3.56</td>\n",
" <td>1.19</td>\n",
" <td>2.38</td>\n",
" <td>2.46</td>\n",
" <td>1.09</td>\n",
" <td>-1.03</td>\n",
" <td>-1.69</td>\n",
" <td>-0.24</td>\n",
" <td>4.88</td>\n",
" <td>2.24</td>\n",
" <td>-2.92</td>\n",
" <td>1.63</td>\n",
" <td>-2.78</td>\n",
" <td>4.62</td>\n",
" <td>-3.95</td>\n",
" <td>0.00</td>\n",
" <td>-6.92</td>\n",
" <td>0.35</td>\n",
" <td>-1.76</td>\n",
" <td>-4.87</td>\n",
" <td>-2.42</td>\n",
" <td>1.91</td>\n",
" <td>2.54</td>\n",
" <td>0.98</td>\n",
" <td>-0.25</td>\n",
" <td>-0.61</td>\n",
" <td>2.14</td>\n",
" <td>7.66</td>\n",
" <td>2.92</td>\n",
" <td>1.73</td>\n",
" <td>0.52</td>\n",
" <td>0.82</td>\n",
" <td>4.33</td>\n",
" <td>-0.64</td>\n",
" <td>2.86</td>\n",
" <td>-2.56</td>\n",
" <td>-0.18</td>\n",
" <td>-2.25</td>\n",
" <td>-1.45</td>\n",
" <td>-3.48</td>\n",
" </tr>\n",
" <tr>\n",
" <th>201609</th>\n",
" <td>-2.92</td>\n",
" <td>1.63</td>\n",
" <td>-2.78</td>\n",
" <td>4.62</td>\n",
" <td>-3.95</td>\n",
" <td>0.00</td>\n",
" <td>-6.92</td>\n",
" <td>0.35</td>\n",
" <td>-1.76</td>\n",
" <td>-4.87</td>\n",
" <td>-2.42</td>\n",
" <td>1.91</td>\n",
" <td>2.54</td>\n",
" <td>0.98</td>\n",
" <td>-0.25</td>\n",
" <td>-0.61</td>\n",
" <td>2.14</td>\n",
" <td>7.66</td>\n",
" <td>2.92</td>\n",
" <td>1.73</td>\n",
" <td>0.52</td>\n",
" <td>0.82</td>\n",
" <td>4.33</td>\n",
" <td>-0.64</td>\n",
" <td>2.86</td>\n",
" <td>-2.56</td>\n",
" <td>-0.18</td>\n",
" <td>-2.25</td>\n",
" <td>-1.45</td>\n",
" <td>-3.48</td>\n",
" <td>-0.33</td>\n",
" <td>-1.65</td>\n",
" <td>4.59</td>\n",
" <td>5.59</td>\n",
" <td>-10.28</td>\n",
" <td>-2.96</td>\n",
" <td>-5.76</td>\n",
" <td>-7.45</td>\n",
" <td>-1.95</td>\n",
" <td>-4.17</td>\n",
" <td>-3.93</td>\n",
" <td>-3.14</td>\n",
" <td>-4.10</td>\n",
" <td>-4.99</td>\n",
" <td>-3.29</td>\n",
" <td>2.21</td>\n",
" <td>0.78</td>\n",
" <td>-8.69</td>\n",
" <td>-2.92</td>\n",
" <td>-0.63</td>\n",
" <td>-2.85</td>\n",
" <td>-0.55</td>\n",
" <td>-2.24</td>\n",
" <td>-5.49</td>\n",
" <td>-0.64</td>\n",
" <td>-8.18</td>\n",
" <td>-3.59</td>\n",
" <td>-1.96</td>\n",
" <td>1.40</td>\n",
" <td>-0.53</td>\n",
" </tr>\n",
" <tr>\n",
" <th>201610</th>\n",
" <td>-0.33</td>\n",
" <td>-1.65</td>\n",
" <td>4.59</td>\n",
" <td>5.59</td>\n",
" <td>-10.28</td>\n",
" <td>-2.96</td>\n",
" <td>-5.76</td>\n",
" <td>-7.45</td>\n",
" <td>-1.95</td>\n",
" <td>-4.17</td>\n",
" <td>-3.93</td>\n",
" <td>-3.14</td>\n",
" <td>-4.10</td>\n",
" <td>-4.99</td>\n",
" <td>-3.29</td>\n",
" <td>2.21</td>\n",
" <td>0.78</td>\n",
" <td>-8.69</td>\n",
" <td>-2.92</td>\n",
" <td>-0.63</td>\n",
" <td>-2.85</td>\n",
" <td>-0.55</td>\n",
" <td>-2.24</td>\n",
" <td>-5.49</td>\n",
" <td>-0.64</td>\n",
" <td>-8.18</td>\n",
" <td>-3.59</td>\n",
" <td>-1.96</td>\n",
" <td>1.40</td>\n",
" <td>-0.53</td>\n",
" <td>-4.41</td>\n",
" <td>-5.76</td>\n",
" <td>-5.12</td>\n",
" <td>3.87</td>\n",
" <td>8.15</td>\n",
" <td>-4.18</td>\n",
" <td>1.80</td>\n",
" <td>1.37</td>\n",
" <td>7.55</td>\n",
" <td>1.58</td>\n",
" <td>9.77</td>\n",
" <td>21.01</td>\n",
" <td>12.44</td>\n",
" <td>8.57</td>\n",
" <td>6.89</td>\n",
" <td>8.79</td>\n",
" <td>11.59</td>\n",
" <td>20.78</td>\n",
" <td>9.52</td>\n",
" <td>-2.85</td>\n",
" <td>6.25</td>\n",
" <td>-0.01</td>\n",
" <td>2.39</td>\n",
" <td>4.21</td>\n",
" <td>12.75</td>\n",
" <td>9.29</td>\n",
" <td>2.99</td>\n",
" <td>8.47</td>\n",
" <td>12.84</td>\n",
" <td>8.29</td>\n",
" </tr>\n",
" <tr>\n",
" <th>201611</th>\n",
" <td>-4.41</td>\n",
" <td>-5.76</td>\n",
" <td>-5.12</td>\n",
" <td>3.87</td>\n",
" <td>8.15</td>\n",
" <td>-4.18</td>\n",
" <td>1.80</td>\n",
" <td>1.37</td>\n",
" <td>7.55</td>\n",
" <td>1.58</td>\n",
" <td>9.77</td>\n",
" <td>21.01</td>\n",
" <td>12.44</td>\n",
" <td>8.57</td>\n",
" <td>6.89</td>\n",
" <td>8.79</td>\n",
" <td>11.59</td>\n",
" <td>20.78</td>\n",
" <td>9.52</td>\n",
" <td>-2.85</td>\n",
" <td>6.25</td>\n",
" <td>-0.01</td>\n",
" <td>2.39</td>\n",
" <td>4.21</td>\n",
" <td>12.75</td>\n",
" <td>9.29</td>\n",
" <td>2.99</td>\n",
" <td>8.47</td>\n",
" <td>12.84</td>\n",
" <td>8.29</td>\n",
" <td>4.43</td>\n",
" <td>3.00</td>\n",
" <td>5.39</td>\n",
" <td>-3.36</td>\n",
" <td>1.98</td>\n",
" <td>1.43</td>\n",
" <td>-0.44</td>\n",
" <td>0.82</td>\n",
" <td>0.32</td>\n",
" <td>-1.27</td>\n",
" <td>0.16</td>\n",
" <td>-2.14</td>\n",
" <td>-0.15</td>\n",
" <td>-0.40</td>\n",
" <td>4.16</td>\n",
" <td>1.40</td>\n",
" <td>-1.80</td>\n",
" <td>-9.68</td>\n",
" <td>2.17</td>\n",
" <td>3.61</td>\n",
" <td>4.65</td>\n",
" <td>-0.19</td>\n",
" <td>2.07</td>\n",
" <td>1.86</td>\n",
" <td>0.84</td>\n",
" <td>2.34</td>\n",
" <td>-0.98</td>\n",
" <td>0.58</td>\n",
" <td>3.80</td>\n",
" <td>2.57</td>\n",
" </tr>\n",
" <tr>\n",
" <th>201612</th>\n",
" <td>4.43</td>\n",
" <td>3.00</td>\n",
" <td>5.39</td>\n",
" <td>-3.36</td>\n",
" <td>1.98</td>\n",
" <td>1.43</td>\n",
" <td>-0.44</td>\n",
" <td>0.82</td>\n",
" <td>0.32</td>\n",
" <td>-1.27</td>\n",
" <td>0.16</td>\n",
" <td>-2.14</td>\n",
" <td>-0.15</td>\n",
" <td>-0.40</td>\n",
" <td>4.16</td>\n",
" <td>1.40</td>\n",
" <td>-1.80</td>\n",
" <td>-9.68</td>\n",
" <td>2.17</td>\n",
" <td>3.61</td>\n",
" <td>4.65</td>\n",
" <td>-0.19</td>\n",
" <td>2.07</td>\n",
" <td>1.86</td>\n",
" <td>0.84</td>\n",
" <td>2.34</td>\n",
" <td>-0.98</td>\n",
" <td>0.58</td>\n",
" <td>3.80</td>\n",
" <td>2.57</td>\n",
" <td>0.95</td>\n",
" <td>-1.02</td>\n",
" <td>5.61</td>\n",
" <td>4.84</td>\n",
" <td>1.60</td>\n",
" <td>2.72</td>\n",
" <td>-0.01</td>\n",
" <td>2.17</td>\n",
" <td>3.79</td>\n",
" <td>6.90</td>\n",
" <td>3.85</td>\n",
" <td>3.60</td>\n",
" <td>4.64</td>\n",
" <td>4.35</td>\n",
" <td>5.18</td>\n",
" <td>1.95</td>\n",
" <td>12.37</td>\n",
" <td>-5.36</td>\n",
" <td>-4.37</td>\n",
" <td>1.05</td>\n",
" <td>3.36</td>\n",
" <td>5.45</td>\n",
" <td>3.20</td>\n",
" <td>2.28</td>\n",
" <td>1.70</td>\n",
" <td>1.69</td>\n",
" <td>0.93</td>\n",
" <td>0.71</td>\n",
" <td>0.56</td>\n",
" <td>-0.87</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"<p>685 rows × 60 columns</p>\n",
"</div>\n",
" <button class=\"colab-df-convert\" onclick=\"convertToInteractive('df-f5d9f629-80aa-450d-9077-34374d1aa85f')\"\n",
" title=\"Convert this dataframe to an interactive table.\"\n",
" style=\"display:none;\">\n",
" \n",
" <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\"viewBox=\"0 0 24 24\"\n",
" width=\"24px\">\n",
" <path d=\"M0 0h24v24H0V0z\" fill=\"none\"/>\n",
" <path d=\"M18.56 5.44l.94 2.06.94-2.06 2.06-.94-2.06-.94-.94-2.06-.94 2.06-2.06.94zm-11 1L8.5 8.5l.94-2.06 2.06-.94-2.06-.94L8.5 2.5l-.94 2.06-2.06.94zm10 10l.94 2.06.94-2.06 2.06-.94-2.06-.94-.94-2.06-.94 2.06-2.06.94z\"/><path d=\"M17.41 7.96l-1.37-1.37c-.4-.4-.92-.59-1.43-.59-.52 0-1.04.2-1.43.59L10.3 9.45l-7.72 7.72c-.78.78-.78 2.05 0 2.83L4 21.41c.39.39.9.59 1.41.59.51 0 1.02-.2 1.41-.59l7.78-7.78 2.81-2.81c.8-.78.8-2.07 0-2.86zM5.41 20L4 18.59l7.72-7.72 1.47 1.35L5.41 20z\"/>\n",
" </svg>\n",
" </button>\n",
" \n",
" <style>\n",
" .colab-df-container {\n",
" display:flex;\n",
" flex-wrap:wrap;\n",
" gap: 12px;\n",
" }\n",
"\n",
" .colab-df-convert {\n",
" background-color: #E8F0FE;\n",
" border: none;\n",
" border-radius: 50%;\n",
" cursor: pointer;\n",
" display: none;\n",
" fill: #1967D2;\n",
" height: 32px;\n",
" padding: 0 0 0 0;\n",
" width: 32px;\n",
" }\n",
"\n",
" .colab-df-convert:hover {\n",
" background-color: #E2EBFA;\n",
" box-shadow: 0px 1px 2px rgba(60, 64, 67, 0.3), 0px 1px 3px 1px rgba(60, 64, 67, 0.15);\n",
" fill: #174EA6;\n",
" }\n",
"\n",
" [theme=dark] .colab-df-convert {\n",
" background-color: #3B4455;\n",
" fill: #D2E3FC;\n",
" }\n",
"\n",
" [theme=dark] .colab-df-convert:hover {\n",
" background-color: #434B5C;\n",
" box-shadow: 0px 1px 3px 1px rgba(0, 0, 0, 0.15);\n",
" filter: drop-shadow(0px 1px 2px rgba(0, 0, 0, 0.3));\n",
" fill: #FFFFFF;\n",
" }\n",
" </style>\n",
"\n",
" <script>\n",
" const buttonEl =\n",
" document.querySelector('#df-f5d9f629-80aa-450d-9077-34374d1aa85f button.colab-df-convert');\n",
" buttonEl.style.display =\n",
" google.colab.kernel.accessAllowed ? 'block' : 'none';\n",
"\n",
" async function convertToInteractive(key) {\n",
" const element = document.querySelector('#df-f5d9f629-80aa-450d-9077-34374d1aa85f');\n",
" const dataTable =\n",
" await google.colab.kernel.invokeFunction('convertToInteractive',\n",
" [key], {});\n",
" if (!dataTable) return;\n",
"\n",
" const docLinkHtml = 'Like what you see? Visit the ' +\n",
" '<a target=\"_blank\" href=https://colab.research.google.com/notebooks/data_table.ipynb>data table notebook</a>'\n",
" + ' to learn more about interactive tables.';\n",
" element.innerHTML = '';\n",
" dataTable['output_type'] = 'display_data';\n",
" await google.colab.output.renderOutput(dataTable, element);\n",
" const docLink = document.createElement('div');\n",
" docLink.innerHTML = docLinkHtml;\n",
" element.appendChild(docLink);\n",
" }\n",
" </script>\n",
" </div>\n",
" </div>\n",
" "
],
"text/plain": [
" Food Beer Smoke Games ... Rtail.lead Meals.lead Fin.lead Other.lead\n",
"yyyymm ... \n",
"195912 2.01 0.35 -3.02 1.64 ... -6.09 -10.08 -4.68 -3.98\n",
"196001 -4.49 -5.71 -2.05 1.21 ... 4.00 1.81 -0.98 6.32\n",
"196002 3.35 -2.14 2.27 4.23 ... -0.13 -3.88 0.05 -2.43\n",
"196003 -1.67 -2.94 -0.18 -0.65 ... -0.53 8.86 -0.64 0.55\n",
"196004 1.17 -2.16 1.35 6.46 ... 3.11 0.80 -0.45 1.02\n",
"... ... ... ... ... ... ... ... ... ...\n",
"201608 -0.52 -0.90 -1.22 0.94 ... -0.18 -2.25 -1.45 -3.48\n",
"201609 -2.92 1.63 -2.78 4.62 ... -3.59 -1.96 1.40 -0.53\n",
"201610 -0.33 -1.65 4.59 5.59 ... 2.99 8.47 12.84 8.29\n",
"201611 -4.41 -5.76 -5.12 3.87 ... -0.98 0.58 3.80 2.57\n",
"201612 4.43 3.00 5.39 -3.36 ... 0.93 0.71 0.56 -0.87\n",
"\n",
"[685 rows x 60 columns]"
]
},
"metadata": {},
"execution_count": 5
}
]
},
{
"cell_type": "code",
"metadata": {
"id": "D8_fqeR-fkX7",
"outputId": "e9efb9e3-276a-4b0c-d263-8f614c557328",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 364
}
},
"source": [
"data.to_csv(\"data.csv\")\n",
"desc = data.describe()\n",
"desc\n",
"# min, max line up with Table 1"
],
"execution_count": null,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/html": [
"\n",
" <div id=\"df-64a4700a-1e83-4129-ac5c-5f9ca7bc05dc\">\n",
" <div class=\"colab-df-container\">\n",
" <div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>Food</th>\n",
" <th>Beer</th>\n",
" <th>Smoke</th>\n",
" <th>Games</th>\n",
" <th>Books</th>\n",
" <th>Hshld</th>\n",
" <th>Clths</th>\n",
" <th>Hlth</th>\n",
" <th>Chems</th>\n",
" <th>Txtls</th>\n",
" <th>Cnstr</th>\n",
" <th>Steel</th>\n",
" <th>FabPr</th>\n",
" <th>ElcEq</th>\n",
" <th>Autos</th>\n",
" <th>Carry</th>\n",
" <th>Mines</th>\n",
" <th>Coal</th>\n",
" <th>Oil</th>\n",
" <th>Util</th>\n",
" <th>Telcm</th>\n",
" <th>Servs</th>\n",
" <th>BusEq</th>\n",
" <th>Paper</th>\n",
" <th>Trans</th>\n",
" <th>Whlsl</th>\n",
" <th>Rtail</th>\n",
" <th>Meals</th>\n",
" <th>Fin</th>\n",
" <th>Other</th>\n",
" <th>Food.lead</th>\n",
" <th>Beer.lead</th>\n",
" <th>Smoke.lead</th>\n",
" <th>Games.lead</th>\n",
" <th>Books.lead</th>\n",
" <th>Hshld.lead</th>\n",
" <th>Clths.lead</th>\n",
" <th>Hlth.lead</th>\n",
" <th>Chems.lead</th>\n",
" <th>Txtls.lead</th>\n",
" <th>Cnstr.lead</th>\n",
" <th>Steel.lead</th>\n",
" <th>FabPr.lead</th>\n",
" <th>ElcEq.lead</th>\n",
" <th>Autos.lead</th>\n",
" <th>Carry.lead</th>\n",
" <th>Mines.lead</th>\n",
" <th>Coal.lead</th>\n",
" <th>Oil.lead</th>\n",
" <th>Util.lead</th>\n",
" <th>Telcm.lead</th>\n",
" <th>Servs.lead</th>\n",
" <th>BusEq.lead</th>\n",
" <th>Paper.lead</th>\n",
" <th>Trans.lead</th>\n",
" <th>Whlsl.lead</th>\n",
" <th>Rtail.lead</th>\n",
" <th>Meals.lead</th>\n",
" <th>Fin.lead</th>\n",
" <th>Other.lead</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>count</th>\n",
" <td>685.000000</td>\n",
" <td>685.000000</td>\n",
" <td>685.000000</td>\n",
" <td>685.000000</td>\n",
" <td>685.000000</td>\n",
" <td>685.000000</td>\n",
" <td>685.000000</td>\n",
" <td>685.000000</td>\n",
" <td>685.000000</td>\n",
" <td>685.000000</td>\n",
" <td>685.000000</td>\n",
" <td>685.000000</td>\n",
" <td>685.000000</td>\n",
" <td>685.000000</td>\n",
" <td>685.000000</td>\n",
" <td>685.000000</td>\n",
" <td>685.000000</td>\n",
" <td>685.000000</td>\n",
" <td>685.000000</td>\n",
" <td>685.000000</td>\n",
" <td>685.000000</td>\n",
" <td>685.000000</td>\n",
" <td>685.000000</td>\n",
" <td>685.000000</td>\n",
" <td>685.000000</td>\n",
" <td>685.000000</td>\n",
" <td>685.000000</td>\n",
" <td>685.000000</td>\n",
" <td>685.000000</td>\n",
" <td>685.000000</td>\n",
" <td>685.000000</td>\n",
" <td>685.000000</td>\n",
" <td>685.00000</td>\n",
" <td>685.00000</td>\n",
" <td>685.000000</td>\n",
" <td>685.000000</td>\n",
" <td>685.000000</td>\n",
" <td>685.000000</td>\n",
" <td>685.000000</td>\n",
" <td>685.000000</td>\n",
" <td>685.000000</td>\n",
" <td>685.000000</td>\n",
" <td>685.000000</td>\n",
" <td>685.000000</td>\n",
" <td>685.000000</td>\n",
" <td>685.000000</td>\n",
" <td>685.000000</td>\n",
" <td>685.000000</td>\n",
" <td>685.000000</td>\n",
" <td>685.000000</td>\n",
" <td>685.000000</td>\n",
" <td>685.000000</td>\n",
" <td>685.000000</td>\n",
" <td>685.000000</td>\n",
" <td>685.000000</td>\n",
" <td>685.000000</td>\n",
" <td>685.000000</td>\n",
" <td>685.000000</td>\n",
" <td>685.000000</td>\n",
" <td>685.000000</td>\n",
" </tr>\n",
" <tr>\n",
" <th>mean</th>\n",
" <td>0.690715</td>\n",
" <td>0.710613</td>\n",
" <td>0.982321</td>\n",
" <td>0.701708</td>\n",
" <td>0.528277</td>\n",
" <td>0.554190</td>\n",
" <td>0.669460</td>\n",
" <td>0.650905</td>\n",
" <td>0.519781</td>\n",
" <td>0.667416</td>\n",
" <td>0.523460</td>\n",
" <td>0.293810</td>\n",
" <td>0.562584</td>\n",
" <td>0.704219</td>\n",
" <td>0.453985</td>\n",
" <td>0.718730</td>\n",
" <td>0.548657</td>\n",
" <td>0.735547</td>\n",
" <td>0.650978</td>\n",
" <td>0.483606</td>\n",
" <td>0.521737</td>\n",
" <td>0.683416</td>\n",
" <td>0.580482</td>\n",
" <td>0.510044</td>\n",
" <td>0.582569</td>\n",
" <td>0.622657</td>\n",
" <td>0.664380</td>\n",
" <td>0.705650</td>\n",
" <td>0.609212</td>\n",
" <td>0.377401</td>\n",
" <td>0.689168</td>\n",
" <td>0.708613</td>\n",
" <td>0.99492</td>\n",
" <td>0.70638</td>\n",
" <td>0.519971</td>\n",
" <td>0.557182</td>\n",
" <td>0.666715</td>\n",
" <td>0.656949</td>\n",
" <td>0.520818</td>\n",
" <td>0.676409</td>\n",
" <td>0.528029</td>\n",
" <td>0.295883</td>\n",
" <td>0.564818</td>\n",
" <td>0.702496</td>\n",
" <td>0.453839</td>\n",
" <td>0.728701</td>\n",
" <td>0.560292</td>\n",
" <td>0.724701</td>\n",
" <td>0.638861</td>\n",
" <td>0.482526</td>\n",
" <td>0.520847</td>\n",
" <td>0.694234</td>\n",
" <td>0.584175</td>\n",
" <td>0.511241</td>\n",
" <td>0.582088</td>\n",
" <td>0.625562</td>\n",
" <td>0.662219</td>\n",
" <td>0.702730</td>\n",
" <td>0.609810</td>\n",
" <td>0.385620</td>\n",
" </tr>\n",
" <tr>\n",
" <th>std</th>\n",
" <td>4.339811</td>\n",
" <td>5.090215</td>\n",
" <td>6.061582</td>\n",
" <td>7.180918</td>\n",
" <td>5.809314</td>\n",
" <td>4.759874</td>\n",
" <td>6.386027</td>\n",
" <td>4.928072</td>\n",
" <td>5.518477</td>\n",
" <td>7.022552</td>\n",
" <td>5.991354</td>\n",
" <td>7.294629</td>\n",
" <td>6.114140</td>\n",
" <td>6.211516</td>\n",
" <td>6.689082</td>\n",
" <td>6.292859</td>\n",
" <td>7.452882</td>\n",
" <td>10.193399</td>\n",
" <td>5.348388</td>\n",
" <td>3.991419</td>\n",
" <td>4.629125</td>\n",
" <td>6.526231</td>\n",
" <td>6.738237</td>\n",
" <td>5.054992</td>\n",
" <td>5.739414</td>\n",
" <td>5.605280</td>\n",
" <td>5.349748</td>\n",
" <td>6.104997</td>\n",
" <td>5.411795</td>\n",
" <td>5.821199</td>\n",
" <td>4.339528</td>\n",
" <td>5.090626</td>\n",
" <td>6.06222</td>\n",
" <td>7.18257</td>\n",
" <td>5.803697</td>\n",
" <td>4.760591</td>\n",
" <td>6.385915</td>\n",
" <td>4.927392</td>\n",
" <td>5.519025</td>\n",
" <td>7.026588</td>\n",
" <td>5.992697</td>\n",
" <td>7.295367</td>\n",
" <td>6.115352</td>\n",
" <td>6.210339</td>\n",
" <td>6.688977</td>\n",
" <td>6.289385</td>\n",
" <td>7.465115</td>\n",
" <td>10.195930</td>\n",
" <td>5.350350</td>\n",
" <td>3.991165</td>\n",
" <td>4.628520</td>\n",
" <td>6.527984</td>\n",
" <td>6.738979</td>\n",
" <td>5.055314</td>\n",
" <td>5.739306</td>\n",
" <td>5.605317</td>\n",
" <td>5.349341</td>\n",
" <td>6.104515</td>\n",
" <td>5.411766</td>\n",
" <td>5.815446</td>\n",
" </tr>\n",
" <tr>\n",
" <th>min</th>\n",
" <td>-18.150000</td>\n",
" <td>-20.190000</td>\n",
" <td>-25.320000</td>\n",
" <td>-33.400000</td>\n",
" <td>-26.560000</td>\n",
" <td>-22.240000</td>\n",
" <td>-31.500000</td>\n",
" <td>-21.060000</td>\n",
" <td>-28.600000</td>\n",
" <td>-33.110000</td>\n",
" <td>-28.740000</td>\n",
" <td>-32.990000</td>\n",
" <td>-31.630000</td>\n",
" <td>-32.800000</td>\n",
" <td>-36.490000</td>\n",
" <td>-31.100000</td>\n",
" <td>-34.550000</td>\n",
" <td>-38.090000</td>\n",
" <td>-18.960000</td>\n",
" <td>-12.940000</td>\n",
" <td>-16.440000</td>\n",
" <td>-28.670000</td>\n",
" <td>-32.070000</td>\n",
" <td>-27.740000</td>\n",
" <td>-28.500000</td>\n",
" <td>-29.250000</td>\n",
" <td>-29.740000</td>\n",
" <td>-31.890000</td>\n",
" <td>-22.530000</td>\n",
" <td>-28.090000</td>\n",
" <td>-18.150000</td>\n",
" <td>-20.190000</td>\n",
" <td>-25.32000</td>\n",
" <td>-33.40000</td>\n",
" <td>-26.560000</td>\n",
" <td>-22.240000</td>\n",
" <td>-31.500000</td>\n",
" <td>-21.060000</td>\n",
" <td>-28.600000</td>\n",
" <td>-33.110000</td>\n",
" <td>-28.740000</td>\n",
" <td>-32.990000</td>\n",
" <td>-31.630000</td>\n",
" <td>-32.800000</td>\n",
" <td>-36.490000</td>\n",
" <td>-31.100000</td>\n",
" <td>-34.550000</td>\n",
" <td>-38.090000</td>\n",
" <td>-18.960000</td>\n",
" <td>-12.940000</td>\n",
" <td>-16.440000</td>\n",
" <td>-28.670000</td>\n",
" <td>-32.070000</td>\n",
" <td>-27.740000</td>\n",
" <td>-28.500000</td>\n",
" <td>-29.250000</td>\n",
" <td>-29.740000</td>\n",
" <td>-31.890000</td>\n",
" <td>-22.530000</td>\n",
" <td>-28.090000</td>\n",
" </tr>\n",
" <tr>\n",
" <th>25%</th>\n",
" <td>-1.640000</td>\n",
" <td>-2.100000</td>\n",
" <td>-2.780000</td>\n",
" <td>-3.490000</td>\n",
" <td>-2.690000</td>\n",
" <td>-2.110000</td>\n",
" <td>-2.810000</td>\n",
" <td>-2.240000</td>\n",
" <td>-2.800000</td>\n",
" <td>-3.200000</td>\n",
" <td>-2.880000</td>\n",
" <td>-4.120000</td>\n",
" <td>-3.050000</td>\n",
" <td>-2.990000</td>\n",
" <td>-3.350000</td>\n",
" <td>-3.220000</td>\n",
" <td>-3.980000</td>\n",
" <td>-5.050000</td>\n",
" <td>-2.660000</td>\n",
" <td>-1.860000</td>\n",
" <td>-2.110000</td>\n",
" <td>-3.090000</td>\n",
" <td>-3.290000</td>\n",
" <td>-2.430000</td>\n",
" <td>-2.780000</td>\n",
" <td>-2.570000</td>\n",
" <td>-2.430000</td>\n",
" <td>-2.940000</td>\n",
" <td>-2.420000</td>\n",
" <td>-3.010000</td>\n",
" <td>-1.640000</td>\n",
" <td>-2.100000</td>\n",
" <td>-2.74000</td>\n",
" <td>-3.49000</td>\n",
" <td>-2.690000</td>\n",
" <td>-2.110000</td>\n",
" <td>-2.810000</td>\n",
" <td>-2.240000</td>\n",
" <td>-2.800000</td>\n",
" <td>-3.200000</td>\n",
" <td>-2.880000</td>\n",
" <td>-4.120000</td>\n",
" <td>-3.050000</td>\n",
" <td>-2.990000</td>\n",
" <td>-3.350000</td>\n",
" <td>-3.200000</td>\n",
" <td>-3.980000</td>\n",
" <td>-5.050000</td>\n",
" <td>-2.670000</td>\n",
" <td>-1.860000</td>\n",
" <td>-2.110000</td>\n",
" <td>-3.090000</td>\n",
" <td>-3.290000</td>\n",
" <td>-2.430000</td>\n",
" <td>-2.780000</td>\n",
" <td>-2.570000</td>\n",
" <td>-2.430000</td>\n",
" <td>-2.940000</td>\n",
" <td>-2.420000</td>\n",
" <td>-2.990000</td>\n",
" </tr>\n",
" <tr>\n",
" <th>50%</th>\n",
" <td>0.740000</td>\n",
" <td>0.710000</td>\n",
" <td>1.280000</td>\n",
" <td>0.890000</td>\n",
" <td>0.510000</td>\n",
" <td>0.750000</td>\n",
" <td>0.690000</td>\n",
" <td>0.750000</td>\n",
" <td>0.670000</td>\n",
" <td>0.630000</td>\n",
" <td>0.610000</td>\n",
" <td>0.220000</td>\n",
" <td>0.860000</td>\n",
" <td>0.550000</td>\n",
" <td>0.260000</td>\n",
" <td>0.930000</td>\n",
" <td>0.600000</td>\n",
" <td>0.400000</td>\n",
" <td>0.650000</td>\n",
" <td>0.620000</td>\n",
" <td>0.610000</td>\n",
" <td>0.960000</td>\n",
" <td>0.560000</td>\n",
" <td>0.690000</td>\n",
" <td>0.860000</td>\n",
" <td>0.900000</td>\n",
" <td>0.470000</td>\n",
" <td>1.040000</td>\n",
" <td>0.820000</td>\n",
" <td>0.470000</td>\n",
" <td>0.740000</td>\n",
" <td>0.710000</td>\n",
" <td>1.29000</td>\n",
" <td>0.89000</td>\n",
" <td>0.510000</td>\n",
" <td>0.780000</td>\n",
" <td>0.680000</td>\n",
" <td>0.760000</td>\n",
" <td>0.670000</td>\n",
" <td>0.630000</td>\n",
" <td>0.610000</td>\n",
" <td>0.220000</td>\n",
" <td>0.860000</td>\n",
" <td>0.550000</td>\n",
" <td>0.260000</td>\n",
" <td>0.950000</td>\n",
" <td>0.600000</td>\n",
" <td>0.380000</td>\n",
" <td>0.630000</td>\n",
" <td>0.620000</td>\n",
" <td>0.610000</td>\n",
" <td>0.970000</td>\n",
" <td>0.560000</td>\n",
" <td>0.690000</td>\n",
" <td>0.860000</td>\n",
" <td>0.940000</td>\n",
" <td>0.470000</td>\n",
" <td>1.030000</td>\n",
" <td>0.820000</td>\n",
" <td>0.470000</td>\n",
" </tr>\n",
" <tr>\n",
" <th>75%</th>\n",
" <td>3.120000</td>\n",
" <td>3.660000</td>\n",
" <td>4.640000</td>\n",
" <td>5.310000</td>\n",
" <td>3.720000</td>\n",
" <td>3.550000</td>\n",
" <td>4.310000</td>\n",
" <td>3.560000</td>\n",
" <td>3.760000</td>\n",
" <td>4.490000</td>\n",
" <td>4.080000</td>\n",
" <td>4.470000</td>\n",
" <td>4.230000</td>\n",
" <td>4.640000</td>\n",
" <td>4.230000</td>\n",
" <td>4.660000</td>\n",
" <td>5.300000</td>\n",
" <td>6.090000</td>\n",
" <td>3.930000</td>\n",
" <td>2.930000</td>\n",
" <td>3.360000</td>\n",
" <td>4.260000</td>\n",
" <td>4.590000</td>\n",
" <td>3.460000</td>\n",
" <td>4.060000</td>\n",
" <td>3.880000</td>\n",
" <td>4.000000</td>\n",
" <td>4.330000</td>\n",
" <td>4.000000</td>\n",
" <td>4.200000</td>\n",
" <td>3.120000</td>\n",
" <td>3.660000</td>\n",
" <td>4.66000</td>\n",
" <td>5.31000</td>\n",
" <td>3.680000</td>\n",
" <td>3.550000</td>\n",
" <td>4.310000</td>\n",
" <td>3.560000</td>\n",
" <td>3.760000</td>\n",
" <td>4.510000</td>\n",
" <td>4.080000</td>\n",
" <td>4.470000</td>\n",
" <td>4.240000</td>\n",
" <td>4.550000</td>\n",
" <td>4.230000</td>\n",
" <td>4.660000</td>\n",
" <td>5.310000</td>\n",
" <td>6.090000</td>\n",
" <td>3.930000</td>\n",
" <td>2.930000</td>\n",
" <td>3.360000</td>\n",
" <td>4.290000</td>\n",
" <td>4.590000</td>\n",
" <td>3.460000</td>\n",
" <td>4.060000</td>\n",
" <td>3.880000</td>\n",
" <td>4.000000</td>\n",
" <td>4.330000</td>\n",
" <td>4.000000</td>\n",
" <td>4.200000</td>\n",
" </tr>\n",
" <tr>\n",
" <th>max</th>\n",
" <td>19.890000</td>\n",
" <td>25.510000</td>\n",
" <td>32.380000</td>\n",
" <td>34.520000</td>\n",
" <td>33.130000</td>\n",
" <td>18.220000</td>\n",
" <td>31.790000</td>\n",
" <td>29.010000</td>\n",
" <td>21.680000</td>\n",
" <td>59.030000</td>\n",
" <td>25.020000</td>\n",
" <td>30.300000</td>\n",
" <td>22.910000</td>\n",
" <td>23.210000</td>\n",
" <td>49.560000</td>\n",
" <td>23.390000</td>\n",
" <td>35.150000</td>\n",
" <td>45.550000</td>\n",
" <td>23.700000</td>\n",
" <td>18.260000</td>\n",
" <td>21.220000</td>\n",
" <td>23.380000</td>\n",
" <td>24.660000</td>\n",
" <td>21.000000</td>\n",
" <td>18.500000</td>\n",
" <td>17.530000</td>\n",
" <td>26.490000</td>\n",
" <td>27.380000</td>\n",
" <td>20.590000</td>\n",
" <td>19.960000</td>\n",
" <td>19.890000</td>\n",
" <td>25.510000</td>\n",
" <td>32.38000</td>\n",
" <td>34.52000</td>\n",
" <td>33.130000</td>\n",
" <td>18.220000</td>\n",
" <td>31.790000</td>\n",
" <td>29.010000</td>\n",
" <td>21.680000</td>\n",
" <td>59.030000</td>\n",
" <td>25.020000</td>\n",
" <td>30.300000</td>\n",
" <td>22.910000</td>\n",
" <td>23.210000</td>\n",
" <td>49.560000</td>\n",
" <td>23.390000</td>\n",
" <td>35.150000</td>\n",
" <td>45.550000</td>\n",
" <td>23.700000</td>\n",
" <td>18.260000</td>\n",
" <td>21.220000</td>\n",
" <td>23.380000</td>\n",
" <td>24.660000</td>\n",
" <td>21.000000</td>\n",
" <td>18.500000</td>\n",
" <td>17.530000</td>\n",
" <td>26.490000</td>\n",
" <td>27.380000</td>\n",
" <td>20.590000</td>\n",
" <td>19.960000</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>\n",
" <button class=\"colab-df-convert\" onclick=\"convertToInteractive('df-64a4700a-1e83-4129-ac5c-5f9ca7bc05dc')\"\n",
" title=\"Convert this dataframe to an interactive table.\"\n",
" style=\"display:none;\">\n",
" \n",
" <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\"viewBox=\"0 0 24 24\"\n",
" width=\"24px\">\n",
" <path d=\"M0 0h24v24H0V0z\" fill=\"none\"/>\n",
" <path d=\"M18.56 5.44l.94 2.06.94-2.06 2.06-.94-2.06-.94-.94-2.06-.94 2.06-2.06.94zm-11 1L8.5 8.5l.94-2.06 2.06-.94-2.06-.94L8.5 2.5l-.94 2.06-2.06.94zm10 10l.94 2.06.94-2.06 2.06-.94-2.06-.94-.94-2.06-.94 2.06-2.06.94z\"/><path d=\"M17.41 7.96l-1.37-1.37c-.4-.4-.92-.59-1.43-.59-.52 0-1.04.2-1.43.59L10.3 9.45l-7.72 7.72c-.78.78-.78 2.05 0 2.83L4 21.41c.39.39.9.59 1.41.59.51 0 1.02-.2 1.41-.59l7.78-7.78 2.81-2.81c.8-.78.8-2.07 0-2.86zM5.41 20L4 18.59l7.72-7.72 1.47 1.35L5.41 20z\"/>\n",
" </svg>\n",
" </button>\n",
" \n",
" <style>\n",
" .colab-df-container {\n",
" display:flex;\n",
" flex-wrap:wrap;\n",
" gap: 12px;\n",
" }\n",
"\n",
" .colab-df-convert {\n",
" background-color: #E8F0FE;\n",
" border: none;\n",
" border-radius: 50%;\n",
" cursor: pointer;\n",
" display: none;\n",
" fill: #1967D2;\n",
" height: 32px;\n",
" padding: 0 0 0 0;\n",
" width: 32px;\n",
" }\n",
"\n",
" .colab-df-convert:hover {\n",
" background-color: #E2EBFA;\n",
" box-shadow: 0px 1px 2px rgba(60, 64, 67, 0.3), 0px 1px 3px 1px rgba(60, 64, 67, 0.15);\n",
" fill: #174EA6;\n",
" }\n",
"\n",
" [theme=dark] .colab-df-convert {\n",
" background-color: #3B4455;\n",
" fill: #D2E3FC;\n",
" }\n",
"\n",
" [theme=dark] .colab-df-convert:hover {\n",
" background-color: #434B5C;\n",
" box-shadow: 0px 1px 3px 1px rgba(0, 0, 0, 0.15);\n",
" filter: drop-shadow(0px 1px 2px rgba(0, 0, 0, 0.3));\n",
" fill: #FFFFFF;\n",
" }\n",
" </style>\n",
"\n",
" <script>\n",
" const buttonEl =\n",
" document.querySelector('#df-64a4700a-1e83-4129-ac5c-5f9ca7bc05dc button.colab-df-convert');\n",
" buttonEl.style.display =\n",
" google.colab.kernel.accessAllowed ? 'block' : 'none';\n",
"\n",
" async function convertToInteractive(key) {\n",
" const element = document.querySelector('#df-64a4700a-1e83-4129-ac5c-5f9ca7bc05dc');\n",
" const dataTable =\n",
" await google.colab.kernel.invokeFunction('convertToInteractive',\n",
" [key], {});\n",
" if (!dataTable) return;\n",
"\n",
" const docLinkHtml = 'Like what you see? Visit the ' +\n",
" '<a target=\"_blank\" href=https://colab.research.google.com/notebooks/data_table.ipynb>data table notebook</a>'\n",
" + ' to learn more about interactive tables.';\n",
" element.innerHTML = '';\n",
" dataTable['output_type'] = 'display_data';\n",
" await google.colab.output.renderOutput(dataTable, element);\n",
" const docLink = document.createElement('div');\n",
" docLink.innerHTML = docLinkHtml;\n",
" element.appendChild(docLink);\n",
" }\n",
" </script>\n",
" </div>\n",
" </div>\n",
" "
],
"text/plain": [
" Food Beer Smoke ... Meals.lead Fin.lead Other.lead\n",
"count 685.000000 685.000000 685.000000 ... 685.000000 685.000000 685.000000\n",
"mean 0.690715 0.710613 0.982321 ... 0.702730 0.609810 0.385620\n",
"std 4.339811 5.090215 6.061582 ... 6.104515 5.411766 5.815446\n",
"min -18.150000 -20.190000 -25.320000 ... -31.890000 -22.530000 -28.090000\n",
"25% -1.640000 -2.100000 -2.780000 ... -2.940000 -2.420000 -2.990000\n",
"50% 0.740000 0.710000 1.280000 ... 1.030000 0.820000 0.470000\n",
"75% 3.120000 3.660000 4.640000 ... 4.330000 4.000000 4.200000\n",
"max 19.890000 25.510000 32.380000 ... 27.380000 20.590000 19.960000\n",
"\n",
"[8 rows x 60 columns]"
]
},
"metadata": {},
"execution_count": 6
}
]
},
{
"cell_type": "code",
"metadata": {
"id": "Im6K_dwCfkYA",
"outputId": "9f575238-4c1b-41fd-bf67-2783754b70dd",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 990
}
},
"source": [
"# annualized returns don't match Table 1, oddly\n",
"# geometric mean, annualized\n",
"pd.DataFrame((np.prod(data/100 + 1)**(12.0/len(data))-1)[:30], columns=['Mean Ann. Return'])"
],
"execution_count": null,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/html": [
"\n",
" <div id=\"df-090516f4-8f0c-4cc0-abab-6366526592cd\">\n",
" <div class=\"colab-df-container\">\n",
" <div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>Mean Ann. Return</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>Food</th>\n",
" <td>0.074020</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Beer</th>\n",
" <td>0.072005</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Smoke</th>\n",
" <td>0.100147</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Games</th>\n",
" <td>0.054031</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Books</th>\n",
" <td>0.043953</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Hshld</th>\n",
" <td>0.054098</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Clths</th>\n",
" <td>0.057170</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Hlth</th>\n",
" <td>0.065463</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Chems</th>\n",
" <td>0.044917</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Txtls</th>\n",
" <td>0.051888</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Cnstr</th>\n",
" <td>0.041836</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Steel</th>\n",
" <td>0.002802</td>\n",
" </tr>\n",
" <tr>\n",
" <th>FabPr</th>\n",
" <td>0.045615</td>\n",
" </tr>\n",
" <tr>\n",
" <th>ElcEq</th>\n",
" <td>0.062927</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Autos</th>\n",
" <td>0.027963</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Carry</th>\n",
" <td>0.063991</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Mines</th>\n",
" <td>0.032527</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Coal</th>\n",
" <td>0.026075</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Oil</th>\n",
" <td>0.062748</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Util</th>\n",
" <td>0.049564</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Telcm</th>\n",
" <td>0.050868</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Servs</th>\n",
" <td>0.057776</td>\n",
" </tr>\n",
" <tr>\n",
" <th>BusEq</th>\n",
" <td>0.042774</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Paper</th>\n",
" <td>0.046776</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Trans</th>\n",
" <td>0.051138</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Whlsl</th>\n",
" <td>0.057056</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Rtail</th>\n",
" <td>0.064258</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Meals</th>\n",
" <td>0.063630</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Fin</th>\n",
" <td>0.056748</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Other</th>\n",
" <td>0.024894</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>\n",
" <button class=\"colab-df-convert\" onclick=\"convertToInteractive('df-090516f4-8f0c-4cc0-abab-6366526592cd')\"\n",
" title=\"Convert this dataframe to an interactive table.\"\n",
" style=\"display:none;\">\n",
" \n",
" <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\"viewBox=\"0 0 24 24\"\n",
" width=\"24px\">\n",
" <path d=\"M0 0h24v24H0V0z\" fill=\"none\"/>\n",
" <path d=\"M18.56 5.44l.94 2.06.94-2.06 2.06-.94-2.06-.94-.94-2.06-.94 2.06-2.06.94zm-11 1L8.5 8.5l.94-2.06 2.06-.94-2.06-.94L8.5 2.5l-.94 2.06-2.06.94zm10 10l.94 2.06.94-2.06 2.06-.94-2.06-.94-.94-2.06-.94 2.06-2.06.94z\"/><path d=\"M17.41 7.96l-1.37-1.37c-.4-.4-.92-.59-1.43-.59-.52 0-1.04.2-1.43.59L10.3 9.45l-7.72 7.72c-.78.78-.78 2.05 0 2.83L4 21.41c.39.39.9.59 1.41.59.51 0 1.02-.2 1.41-.59l7.78-7.78 2.81-2.81c.8-.78.8-2.07 0-2.86zM5.41 20L4 18.59l7.72-7.72 1.47 1.35L5.41 20z\"/>\n",
" </svg>\n",
" </button>\n",
" \n",
" <style>\n",
" .colab-df-container {\n",
" display:flex;\n",
" flex-wrap:wrap;\n",
" gap: 12px;\n",
" }\n",
"\n",
" .colab-df-convert {\n",
" background-color: #E8F0FE;\n",
" border: none;\n",
" border-radius: 50%;\n",
" cursor: pointer;\n",
" display: none;\n",
" fill: #1967D2;\n",
" height: 32px;\n",
" padding: 0 0 0 0;\n",
" width: 32px;\n",
" }\n",
"\n",
" .colab-df-convert:hover {\n",
" background-color: #E2EBFA;\n",
" box-shadow: 0px 1px 2px rgba(60, 64, 67, 0.3), 0px 1px 3px 1px rgba(60, 64, 67, 0.15);\n",
" fill: #174EA6;\n",
" }\n",
"\n",
" [theme=dark] .colab-df-convert {\n",
" background-color: #3B4455;\n",
" fill: #D2E3FC;\n",
" }\n",
"\n",
" [theme=dark] .colab-df-convert:hover {\n",
" background-color: #434B5C;\n",
" box-shadow: 0px 1px 3px 1px rgba(0, 0, 0, 0.15);\n",
" filter: drop-shadow(0px 1px 2px rgba(0, 0, 0, 0.3));\n",
" fill: #FFFFFF;\n",
" }\n",
" </style>\n",
"\n",
" <script>\n",
" const buttonEl =\n",
" document.querySelector('#df-090516f4-8f0c-4cc0-abab-6366526592cd button.colab-df-convert');\n",
" buttonEl.style.display =\n",
" google.colab.kernel.accessAllowed ? 'block' : 'none';\n",
"\n",
" async function convertToInteractive(key) {\n",
" const element = document.querySelector('#df-090516f4-8f0c-4cc0-abab-6366526592cd');\n",
" const dataTable =\n",
" await google.colab.kernel.invokeFunction('convertToInteractive',\n",
" [key], {});\n",
" if (!dataTable) return;\n",
"\n",
" const docLinkHtml = 'Like what you see? Visit the ' +\n",
" '<a target=\"_blank\" href=https://colab.research.google.com/notebooks/data_table.ipynb>data table notebook</a>'\n",
" + ' to learn more about interactive tables.';\n",
" element.innerHTML = '';\n",
" dataTable['output_type'] = 'display_data';\n",
" await google.colab.output.renderOutput(dataTable, element);\n",
" const docLink = document.createElement('div');\n",
" docLink.innerHTML = docLinkHtml;\n",
" element.appendChild(docLink);\n",
" }\n",
" </script>\n",
" </div>\n",
" </div>\n",
" "
],
"text/plain": [
" Mean Ann. Return\n",
"Food 0.074020\n",
"Beer 0.072005\n",
"Smoke 0.100147\n",
"Games 0.054031\n",
"Books 0.043953\n",
"Hshld 0.054098\n",
"Clths 0.057170\n",
"Hlth 0.065463\n",
"Chems 0.044917\n",
"Txtls 0.051888\n",
"Cnstr 0.041836\n",
"Steel 0.002802\n",
"FabPr 0.045615\n",
"ElcEq 0.062927\n",
"Autos 0.027963\n",
"Carry 0.063991\n",
"Mines 0.032527\n",
"Coal 0.026075\n",
"Oil 0.062748\n",
"Util 0.049564\n",
"Telcm 0.050868\n",
"Servs 0.057776\n",
"BusEq 0.042774\n",
"Paper 0.046776\n",
"Trans 0.051138\n",
"Whlsl 0.057056\n",
"Rtail 0.064258\n",
"Meals 0.063630\n",
"Fin 0.056748\n",
"Other 0.024894"
]
},
"metadata": {},
"execution_count": 7
}
]
},
{
"cell_type": "code",
"metadata": {
"id": "J-8ZJm63fkYE",
"outputId": "d2b1f898-690c-407a-bfb4-e1861936eef4",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 990
}
},
"source": [
"# try this way, arithmetic mean then annualize (not very correct)\n",
"#print(pd.DataFrame(((desc.loc['mean']/100+1)**12-1)[:30]))\n",
"#nope\n",
"\n",
"# same\n",
"pd.DataFrame(((1 + np.mean(data, axis=0)/100)**12 -1)[:30], columns=['Mean Ann. Return'])"
],
"execution_count": null,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/html": [
"\n",
" <div id=\"df-b962da6a-4658-4cdc-b17a-85b5356e0fcc\">\n",
" <div class=\"colab-df-container\">\n",
" <div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>Mean Ann. Return</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>Food</th>\n",
" <td>0.086108</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Beer</th>\n",
" <td>0.088687</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Smoke</th>\n",
" <td>0.124460</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Games</th>\n",
" <td>0.087532</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Books</th>\n",
" <td>0.065268</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Hshld</th>\n",
" <td>0.068568</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Clths</th>\n",
" <td>0.083360</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Hlth</th>\n",
" <td>0.080966</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Chems</th>\n",
" <td>0.064188</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Txtls</th>\n",
" <td>0.083096</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Cnstr</th>\n",
" <td>0.064656</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Steel</th>\n",
" <td>0.035833</td>\n",
" </tr>\n",
" <tr>\n",
" <th>FabPr</th>\n",
" <td>0.069639</td>\n",
" </tr>\n",
" <tr>\n",
" <th>ElcEq</th>\n",
" <td>0.087857</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Autos</th>\n",
" <td>0.055859</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Carry</th>\n",
" <td>0.089740</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Mines</th>\n",
" <td>0.067862</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Coal</th>\n",
" <td>0.091926</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Oil</th>\n",
" <td>0.080976</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Util</th>\n",
" <td>0.059601</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Telcm</th>\n",
" <td>0.064437</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Servs</th>\n",
" <td>0.085164</td>\n",
" </tr>\n",
" <tr>\n",
" <th>BusEq</th>\n",
" <td>0.071925</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Paper</th>\n",
" <td>0.062952</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Trans</th>\n",
" <td>0.072192</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Whlsl</th>\n",
" <td>0.077332</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Rtail</th>\n",
" <td>0.082704</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Meals</th>\n",
" <td>0.088043</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Fin</th>\n",
" <td>0.075605</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Other</th>\n",
" <td>0.046240</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>\n",
" <button class=\"colab-df-convert\" onclick=\"convertToInteractive('df-b962da6a-4658-4cdc-b17a-85b5356e0fcc')\"\n",
" title=\"Convert this dataframe to an interactive table.\"\n",
" style=\"display:none;\">\n",
" \n",
" <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\"viewBox=\"0 0 24 24\"\n",
" width=\"24px\">\n",
" <path d=\"M0 0h24v24H0V0z\" fill=\"none\"/>\n",
" <path d=\"M18.56 5.44l.94 2.06.94-2.06 2.06-.94-2.06-.94-.94-2.06-.94 2.06-2.06.94zm-11 1L8.5 8.5l.94-2.06 2.06-.94-2.06-.94L8.5 2.5l-.94 2.06-2.06.94zm10 10l.94 2.06.94-2.06 2.06-.94-2.06-.94-.94-2.06-.94 2.06-2.06.94z\"/><path d=\"M17.41 7.96l-1.37-1.37c-.4-.4-.92-.59-1.43-.59-.52 0-1.04.2-1.43.59L10.3 9.45l-7.72 7.72c-.78.78-.78 2.05 0 2.83L4 21.41c.39.39.9.59 1.41.59.51 0 1.02-.2 1.41-.59l7.78-7.78 2.81-2.81c.8-.78.8-2.07 0-2.86zM5.41 20L4 18.59l7.72-7.72 1.47 1.35L5.41 20z\"/>\n",
" </svg>\n",
" </button>\n",
" \n",
" <style>\n",
" .colab-df-container {\n",
" display:flex;\n",
" flex-wrap:wrap;\n",
" gap: 12px;\n",
" }\n",
"\n",
" .colab-df-convert {\n",
" background-color: #E8F0FE;\n",
" border: none;\n",
" border-radius: 50%;\n",
" cursor: pointer;\n",
" display: none;\n",
" fill: #1967D2;\n",
" height: 32px;\n",
" padding: 0 0 0 0;\n",
" width: 32px;\n",
" }\n",
"\n",
" .colab-df-convert:hover {\n",
" background-color: #E2EBFA;\n",
" box-shadow: 0px 1px 2px rgba(60, 64, 67, 0.3), 0px 1px 3px 1px rgba(60, 64, 67, 0.15);\n",
" fill: #174EA6;\n",
" }\n",
"\n",
" [theme=dark] .colab-df-convert {\n",
" background-color: #3B4455;\n",
" fill: #D2E3FC;\n",
" }\n",
"\n",
" [theme=dark] .colab-df-convert:hover {\n",
" background-color: #434B5C;\n",
" box-shadow: 0px 1px 3px 1px rgba(0, 0, 0, 0.15);\n",
" filter: drop-shadow(0px 1px 2px rgba(0, 0, 0, 0.3));\n",
" fill: #FFFFFF;\n",
" }\n",
" </style>\n",
"\n",
" <script>\n",
" const buttonEl =\n",
" document.querySelector('#df-b962da6a-4658-4cdc-b17a-85b5356e0fcc button.colab-df-convert');\n",
" buttonEl.style.display =\n",
" google.colab.kernel.accessAllowed ? 'block' : 'none';\n",
"\n",
" async function convertToInteractive(key) {\n",
" const element = document.querySelector('#df-b962da6a-4658-4cdc-b17a-85b5356e0fcc');\n",
" const dataTable =\n",
" await google.colab.kernel.invokeFunction('convertToInteractive',\n",
" [key], {});\n",
" if (!dataTable) return;\n",
"\n",
" const docLinkHtml = 'Like what you see? Visit the ' +\n",
" '<a target=\"_blank\" href=https://colab.research.google.com/notebooks/data_table.ipynb>data table notebook</a>'\n",
" + ' to learn more about interactive tables.';\n",
" element.innerHTML = '';\n",
" dataTable['output_type'] = 'display_data';\n",
" await google.colab.output.renderOutput(dataTable, element);\n",
" const docLink = document.createElement('div');\n",
" docLink.innerHTML = docLinkHtml;\n",
" element.appendChild(docLink);\n",
" }\n",
" </script>\n",
" </div>\n",
" </div>\n",
" "
],
"text/plain": [
" Mean Ann. Return\n",
"Food 0.086108\n",
"Beer 0.088687\n",
"Smoke 0.124460\n",
"Games 0.087532\n",
"Books 0.065268\n",
"Hshld 0.068568\n",
"Clths 0.083360\n",
"Hlth 0.080966\n",
"Chems 0.064188\n",
"Txtls 0.083096\n",
"Cnstr 0.064656\n",
"Steel 0.035833\n",
"FabPr 0.069639\n",
"ElcEq 0.087857\n",
"Autos 0.055859\n",
"Carry 0.089740\n",
"Mines 0.067862\n",
"Coal 0.091926\n",
"Oil 0.080976\n",
"Util 0.059601\n",
"Telcm 0.064437\n",
"Servs 0.085164\n",
"BusEq 0.071925\n",
"Paper 0.062952\n",
"Trans 0.072192\n",
"Whlsl 0.077332\n",
"Rtail 0.082704\n",
"Meals 0.088043\n",
"Fin 0.075605\n",
"Other 0.046240"
]
},
"metadata": {},
"execution_count": 8
}
]
},
{
"cell_type": "code",
"metadata": {
"id": "hhElRHePfkYL",
"outputId": "d77dd273-8277-427a-c8d0-f26a13b1a1e2",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 990
}
},
"source": [
"#annualized volatility\n",
"pd.DataFrame((desc.loc['std']*np.sqrt(12))[:30].round(2))\n",
"# lines up with table 1"
],
"execution_count": null,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/html": [
"\n",
" <div id=\"df-1cd09d16-82be-460b-8cca-3f7460e01d97\">\n",
" <div class=\"colab-df-container\">\n",
" <div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>std</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>Food</th>\n",
" <td>15.03</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Beer</th>\n",
" <td>17.63</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Smoke</th>\n",
" <td>21.00</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Games</th>\n",
" <td>24.88</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Books</th>\n",
" <td>20.12</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Hshld</th>\n",
" <td>16.49</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Clths</th>\n",
" <td>22.12</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Hlth</th>\n",
" <td>17.07</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Chems</th>\n",
" <td>19.12</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Txtls</th>\n",
" <td>24.33</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Cnstr</th>\n",
" <td>20.75</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Steel</th>\n",
" <td>25.27</td>\n",
" </tr>\n",
" <tr>\n",
" <th>FabPr</th>\n",
" <td>21.18</td>\n",
" </tr>\n",
" <tr>\n",
" <th>ElcEq</th>\n",
" <td>21.52</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Autos</th>\n",
" <td>23.17</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Carry</th>\n",
" <td>21.80</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Mines</th>\n",
" <td>25.82</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Coal</th>\n",
" <td>35.31</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Oil</th>\n",
" <td>18.53</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Util</th>\n",
" <td>13.83</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Telcm</th>\n",
" <td>16.04</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Servs</th>\n",
" <td>22.61</td>\n",
" </tr>\n",
" <tr>\n",
" <th>BusEq</th>\n",
" <td>23.34</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Paper</th>\n",
" <td>17.51</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Trans</th>\n",
" <td>19.88</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Whlsl</th>\n",
" <td>19.42</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Rtail</th>\n",
" <td>18.53</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Meals</th>\n",
" <td>21.15</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Fin</th>\n",
" <td>18.75</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Other</th>\n",
" <td>20.17</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>\n",
" <button class=\"colab-df-convert\" onclick=\"convertToInteractive('df-1cd09d16-82be-460b-8cca-3f7460e01d97')\"\n",
" title=\"Convert this dataframe to an interactive table.\"\n",
" style=\"display:none;\">\n",
" \n",
" <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\"viewBox=\"0 0 24 24\"\n",
" width=\"24px\">\n",
" <path d=\"M0 0h24v24H0V0z\" fill=\"none\"/>\n",
" <path d=\"M18.56 5.44l.94 2.06.94-2.06 2.06-.94-2.06-.94-.94-2.06-.94 2.06-2.06.94zm-11 1L8.5 8.5l.94-2.06 2.06-.94-2.06-.94L8.5 2.5l-.94 2.06-2.06.94zm10 10l.94 2.06.94-2.06 2.06-.94-2.06-.94-.94-2.06-.94 2.06-2.06.94z\"/><path d=\"M17.41 7.96l-1.37-1.37c-.4-.4-.92-.59-1.43-.59-.52 0-1.04.2-1.43.59L10.3 9.45l-7.72 7.72c-.78.78-.78 2.05 0 2.83L4 21.41c.39.39.9.59 1.41.59.51 0 1.02-.2 1.41-.59l7.78-7.78 2.81-2.81c.8-.78.8-2.07 0-2.86zM5.41 20L4 18.59l7.72-7.72 1.47 1.35L5.41 20z\"/>\n",
" </svg>\n",
" </button>\n",
" \n",
" <style>\n",
" .colab-df-container {\n",
" display:flex;\n",
" flex-wrap:wrap;\n",
" gap: 12px;\n",
" }\n",
"\n",
" .colab-df-convert {\n",
" background-color: #E8F0FE;\n",
" border: none;\n",
" border-radius: 50%;\n",
" cursor: pointer;\n",
" display: none;\n",
" fill: #1967D2;\n",
" height: 32px;\n",
" padding: 0 0 0 0;\n",
" width: 32px;\n",
" }\n",
"\n",
" .colab-df-convert:hover {\n",
" background-color: #E2EBFA;\n",
" box-shadow: 0px 1px 2px rgba(60, 64, 67, 0.3), 0px 1px 3px 1px rgba(60, 64, 67, 0.15);\n",
" fill: #174EA6;\n",
" }\n",
"\n",
" [theme=dark] .colab-df-convert {\n",
" background-color: #3B4455;\n",
" fill: #D2E3FC;\n",
" }\n",
"\n",
" [theme=dark] .colab-df-convert:hover {\n",
" background-color: #434B5C;\n",
" box-shadow: 0px 1px 3px 1px rgba(0, 0, 0, 0.15);\n",
" filter: drop-shadow(0px 1px 2px rgba(0, 0, 0, 0.3));\n",
" fill: #FFFFFF;\n",
" }\n",
" </style>\n",
"\n",
" <script>\n",
" const buttonEl =\n",
" document.querySelector('#df-1cd09d16-82be-460b-8cca-3f7460e01d97 button.colab-df-convert');\n",
" buttonEl.style.display =\n",
" google.colab.kernel.accessAllowed ? 'block' : 'none';\n",
"\n",
" async function convertToInteractive(key) {\n",
" const element = document.querySelector('#df-1cd09d16-82be-460b-8cca-3f7460e01d97');\n",
" const dataTable =\n",
" await google.colab.kernel.invokeFunction('convertToInteractive',\n",
" [key], {});\n",
" if (!dataTable) return;\n",
"\n",
" const docLinkHtml = 'Like what you see? Visit the ' +\n",
" '<a target=\"_blank\" href=https://colab.research.google.com/notebooks/data_table.ipynb>data table notebook</a>'\n",
" + ' to learn more about interactive tables.';\n",
" element.innerHTML = '';\n",
" dataTable['output_type'] = 'display_data';\n",
" await google.colab.output.renderOutput(dataTable, element);\n",
" const docLink = document.createElement('div');\n",
" docLink.innerHTML = docLinkHtml;\n",
" element.appendChild(docLink);\n",
" }\n",
" </script>\n",
" </div>\n",
" </div>\n",
" "
],
"text/plain": [
" std\n",
"Food 15.03\n",
"Beer 17.63\n",
"Smoke 21.00\n",
"Games 24.88\n",
"Books 20.12\n",
"Hshld 16.49\n",
"Clths 22.12\n",
"Hlth 17.07\n",
"Chems 19.12\n",
"Txtls 24.33\n",
"Cnstr 20.75\n",
"Steel 25.27\n",
"FabPr 21.18\n",
"ElcEq 21.52\n",
"Autos 23.17\n",
"Carry 21.80\n",
"Mines 25.82\n",
"Coal 35.31\n",
"Oil 18.53\n",
"Util 13.83\n",
"Telcm 16.04\n",
"Servs 22.61\n",
"BusEq 23.34\n",
"Paper 17.51\n",
"Trans 19.88\n",
"Whlsl 19.42\n",
"Rtail 18.53\n",
"Meals 21.15\n",
"Fin 18.75\n",
"Other 20.17"
]
},
"metadata": {},
"execution_count": 9
}
]
},
{
"cell_type": "code",
"metadata": {
"id": "yX5HPyuR5M2E",
"outputId": "9b6cdef7-269a-4c7d-a933-cb4e0c3004fd",
"colab": {
"base_uri": "https://localhost:8080/"
}
},
"source": [
"npredictors"
],
"execution_count": null,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"30"
]
},
"metadata": {},
"execution_count": 10
}
]
},
{
"cell_type": "code",
"metadata": {
"id": "5vs1PCfIfkYP",
"outputId": "6e1fd1a9-61c9-4308-8cd9-eeadaad6ae52",
"colab": {
"base_uri": "https://localhost:8080/"
}
},
"source": [
"# Run LASSO, then OLS on selected variables\n",
"\n",
"# skip last row to better match published r-squared\n",
"# looks like they forecast actuals 1960-2016 using 1959m12 to 2016m11\n",
"# not exact matches to Table 2 R-squared but almost within rounding error\n",
"X = data.values[:-1,:npredictors]\n",
"Y = data.values[:-1,-nresponses:] # Predict leads\n",
"nrows = X.shape[0]\n",
"X.shape"
],
"execution_count": null,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"(684, 30)"
]
},
"metadata": {},
"execution_count": 11
}
]
},
{
"cell_type": "code",
"metadata": {
"scrolled": false,
"id": "EdmjvklBfkYV",
"outputId": "d8db25e5-0b72-4813-bc1f-31365ccd5b55",
"colab": {
"base_uri": "https://localhost:8080/"
}
},
"source": [
"def subset_selection(X, Y, model_aic, verbose=False, responses=responses, predictors=predictors):\n",
" # For each response variable run AIC\n",
" nrows, npreds = X.shape\n",
" nows, nresps = Y.shape\n",
" coef_dict = []\n",
"\n",
" for response_index in range(nresps):\n",
" y = Y[:,response_index]\n",
" model_aic.fit(X, y)\n",
" predcols = [i for i in range(npreds) if model_aic.coef_[i] !=0]\n",
"\n",
" #y_response = model_aic.predict(X)\n",
" # print (\"In-sample LASSO R-squared: %.6f\" % r2_score(y, y_response))\n",
" if verbose and responses:\n",
" print(\"LASSO variables selected for %s: \" % responses[response_index])\n",
" print([predictors[i] for i in predcols])\n",
"\n",
" if not predcols:\n",
" if verbose and responses:\n",
" print(\"No coefs selected for \" + responses[response_index] + \", using all\")\n",
" print(\"---\")\n",
" predcols = list(range(npreds))\n",
"\n",
" # fit OLS vs. selected vars, better fit w/o LASSO penalties\n",
" # in-sample R-squared using LASSO coeffs\n",
" coef_dict.append(predcols)\n",
" if verbose and responses and predictors:\n",
" print(\"Running OLS for \" + responses[response_index] + \" against \" + str([predictors[i] for i in predcols]))\n",
" # col nums of selected responses\n",
" model_ols = LinearRegression()\n",
" model_ols.fit(X[:, predcols], y)\n",
" y_pred = model_ols.predict(X[:, predcols])\n",
" print (\"In-sample OLS R-squared: %.2f%%\" % (100 * r2_score(y, y_pred)))\n",
" print(\"---\")\n",
"\n",
" return coef_dict\n",
"\n",
"#coef_dict = subset_selection(X, Y, LassoLarsIC(criterion='aic'))\n",
"coef_dict = subset_selection(X, Y, LassoLarsIC(criterion='aic'), verbose=True, responses=responses, predictors=predictors)\n",
"print(coef_dict)\n",
"# These subsets line up closely with Table 2\n",
"# except Clths, Whlsl, we get different responses"
],
"execution_count": null,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"LASSO variables selected for Food.lead: \n",
"['Clths', 'Coal', 'Util', 'Rtail']\n",
"Running OLS for Food.lead against ['Clths', 'Coal', 'Util', 'Rtail']\n",
"In-sample OLS R-squared: 2.24%\n",
"---\n",
"LASSO variables selected for Beer.lead: \n",
"['Food', 'Clths', 'Coal']\n",
"Running OLS for Beer.lead against ['Food', 'Clths', 'Coal']\n",
"In-sample OLS R-squared: 2.52%\n",
"---\n",
"LASSO variables selected for Smoke.lead: \n",
"['Txtls', 'Carry', 'Mines', 'Coal', 'Oil', 'Util', 'Telcm', 'Servs', 'Paper', 'Trans', 'Fin']\n",
"Running OLS for Smoke.lead against ['Txtls', 'Carry', 'Mines', 'Coal', 'Oil', 'Util', 'Telcm', 'Servs', 'Paper', 'Trans', 'Fin']\n",
"In-sample OLS R-squared: 6.55%\n",
"---\n",
"LASSO variables selected for Games.lead: \n",
"['Books', 'Clths', 'Coal', 'Fin']\n",
"Running OLS for Games.lead against ['Books', 'Clths', 'Coal', 'Fin']\n",
"In-sample OLS R-squared: 5.05%\n",
"---\n",
"LASSO variables selected for Books.lead: \n",
"['Games', 'Books', 'Coal', 'Oil', 'Util', 'Servs', 'BusEq', 'Rtail', 'Fin']\n",
"Running OLS for Books.lead against ['Games', 'Books', 'Coal', 'Oil', 'Util', 'Servs', 'BusEq', 'Rtail', 'Fin']\n",
"In-sample OLS R-squared: 6.30%\n",
"---\n",
"LASSO variables selected for Hshld.lead: \n",
"['Clths', 'Coal', 'Rtail']\n",
"Running OLS for Hshld.lead against ['Clths', 'Coal', 'Rtail']\n",
"In-sample OLS R-squared: 2.97%\n",
"---\n",
"LASSO variables selected for Clths.lead: \n",
"['Clths', 'Coal', 'Oil', 'Servs', 'Rtail']\n",
"Running OLS for Clths.lead against ['Clths', 'Coal', 'Oil', 'Servs', 'Rtail']\n",
"In-sample OLS R-squared: 4.73%\n",
"---\n",
"LASSO variables selected for Hlth.lead: \n",
"['Books', 'Mines', 'Coal', 'Util']\n",
"Running OLS for Hlth.lead against ['Books', 'Mines', 'Coal', 'Util']\n",
"In-sample OLS R-squared: 2.68%\n",
"---\n",
"LASSO variables selected for Chems.lead: \n",
"['Clths']\n",
"Running OLS for Chems.lead against ['Clths']\n",
"In-sample OLS R-squared: 0.78%\n",
"---\n",
"LASSO variables selected for Txtls.lead: \n",
"['Clths', 'Autos', 'Coal', 'Oil', 'Rtail', 'Fin']\n",
"Running OLS for Txtls.lead against ['Clths', 'Autos', 'Coal', 'Oil', 'Rtail', 'Fin']\n",
"In-sample OLS R-squared: 7.91%\n",
"---\n",
"LASSO variables selected for Cnstr.lead: \n",
"['Clths', 'Coal', 'Oil', 'Util', 'Trans', 'Rtail', 'Fin']\n",
"Running OLS for Cnstr.lead against ['Clths', 'Coal', 'Oil', 'Util', 'Trans', 'Rtail', 'Fin']\n",
"In-sample OLS R-squared: 5.15%\n",
"---\n",
"LASSO variables selected for Steel.lead: \n",
"['Fin']\n",
"Running OLS for Steel.lead against ['Fin']\n",
"In-sample OLS R-squared: 1.28%\n",
"---\n",
"LASSO variables selected for FabPr.lead: \n",
"['Trans', 'Fin']\n",
"Running OLS for FabPr.lead against ['Trans', 'Fin']\n",
"In-sample OLS R-squared: 1.57%\n",
"---\n",
"LASSO variables selected for ElcEq.lead: \n",
"['Fin']\n",
"Running OLS for ElcEq.lead against ['Fin']\n",
"In-sample OLS R-squared: 0.80%\n",
"---\n",
"LASSO variables selected for Autos.lead: \n",
"['Hshld', 'Clths', 'Coal', 'Oil', 'Util', 'BusEq', 'Rtail', 'Fin']\n",
"Running OLS for Autos.lead against ['Hshld', 'Clths', 'Coal', 'Oil', 'Util', 'BusEq', 'Rtail', 'Fin']\n",
"In-sample OLS R-squared: 6.13%\n",
"---\n",
"LASSO variables selected for Carry.lead: \n",
"['Trans']\n",
"Running OLS for Carry.lead against ['Trans']\n",
"In-sample OLS R-squared: 2.32%\n",
"---\n",
"LASSO variables selected for Mines.lead: \n",
"[]\n",
"No coefs selected for Mines.lead, using all\n",
"---\n",
"Running OLS for Mines.lead against ['Food', 'Beer', 'Smoke', 'Games', 'Books', 'Hshld', 'Clths', 'Hlth', 'Chems', 'Txtls', 'Cnstr', 'Steel', 'FabPr', 'ElcEq', 'Autos', 'Carry', 'Mines', 'Coal', 'Oil', 'Util', 'Telcm', 'Servs', 'BusEq', 'Paper', 'Trans', 'Whlsl', 'Rtail', 'Meals', 'Fin', 'Other']\n",
"In-sample OLS R-squared: 5.62%\n",
"---\n",
"LASSO variables selected for Coal.lead: \n",
"['Beer', 'Smoke', 'Books', 'Autos', 'Coal', 'Oil', 'Paper', 'Rtail']\n",
"Running OLS for Coal.lead against ['Beer', 'Smoke', 'Books', 'Autos', 'Coal', 'Oil', 'Paper', 'Rtail']\n",
"In-sample OLS R-squared: 2.84%\n",
"---\n",
"LASSO variables selected for Oil.lead: \n",
"['Beer', 'Hlth', 'Carry']\n",
"Running OLS for Oil.lead against ['Beer', 'Hlth', 'Carry']\n",
"In-sample OLS R-squared: 2.52%\n",
"---\n",
"LASSO variables selected for Util.lead: \n",
"['Food', 'Beer', 'Smoke', 'Hshld', 'Hlth', 'Cnstr', 'FabPr', 'Carry', 'Mines', 'Oil', 'Util', 'Telcm', 'BusEq', 'Whlsl', 'Fin', 'Other']\n",
"Running OLS for Util.lead against ['Food', 'Beer', 'Smoke', 'Hshld', 'Hlth', 'Cnstr', 'FabPr', 'Carry', 'Mines', 'Oil', 'Util', 'Telcm', 'BusEq', 'Whlsl', 'Fin', 'Other']\n",
"In-sample OLS R-squared: 7.86%\n",
"---\n",
"LASSO variables selected for Telcm.lead: \n",
"['Beer', 'Smoke', 'Books', 'Hshld', 'Cnstr', 'Autos', 'Carry', 'Mines', 'Coal', 'Oil', 'Util', 'Servs', 'BusEq', 'Rtail', 'Meals', 'Fin']\n",
"Running OLS for Telcm.lead against ['Beer', 'Smoke', 'Books', 'Hshld', 'Cnstr', 'Autos', 'Carry', 'Mines', 'Coal', 'Oil', 'Util', 'Servs', 'BusEq', 'Rtail', 'Meals', 'Fin']\n",
"In-sample OLS R-squared: 5.18%\n",
"---\n",
"LASSO variables selected for Servs.lead: \n",
"['Smoke', 'Books', 'Steel', 'Oil', 'Util', 'Fin']\n",
"Running OLS for Servs.lead against ['Smoke', 'Books', 'Steel', 'Oil', 'Util', 'Fin']\n",
"In-sample OLS R-squared: 2.87%\n",
"---\n",
"LASSO variables selected for BusEq.lead: \n",
"['Smoke', 'Books', 'Util']\n",
"Running OLS for BusEq.lead against ['Smoke', 'Books', 'Util']\n",
"In-sample OLS R-squared: 2.75%\n",
"---\n",
"LASSO variables selected for Paper.lead: \n",
"['Games', 'Books', 'Clths', 'ElcEq', 'Carry', 'Coal', 'Oil', 'Util', 'Servs', 'BusEq', 'Paper', 'Trans', 'Whlsl', 'Rtail', 'Fin']\n",
"Running OLS for Paper.lead against ['Games', 'Books', 'Clths', 'ElcEq', 'Carry', 'Coal', 'Oil', 'Util', 'Servs', 'BusEq', 'Paper', 'Trans', 'Whlsl', 'Rtail', 'Fin']\n",
"In-sample OLS R-squared: 6.30%\n",
"---\n",
"LASSO variables selected for Trans.lead: \n",
"['Beer', 'Smoke', 'Games', 'Books', 'Hshld', 'Clths', 'Hlth', 'Steel', 'FabPr', 'ElcEq', 'Coal', 'Oil', 'Util', 'Telcm', 'Servs', 'Trans', 'Whlsl', 'Rtail', 'Fin', 'Other']\n",
"Running OLS for Trans.lead against ['Beer', 'Smoke', 'Games', 'Books', 'Hshld', 'Clths', 'Hlth', 'Steel', 'FabPr', 'ElcEq', 'Coal', 'Oil', 'Util', 'Telcm', 'Servs', 'Trans', 'Whlsl', 'Rtail', 'Fin', 'Other']\n",
"In-sample OLS R-squared: 7.03%\n",
"---\n",
"LASSO variables selected for Whlsl.lead: \n",
"['Food', 'Beer', 'Smoke', 'Books', 'Hlth', 'Carry', 'Coal', 'Oil', 'Util', 'Telcm', 'Servs', 'BusEq', 'Fin', 'Other']\n",
"Running OLS for Whlsl.lead against ['Food', 'Beer', 'Smoke', 'Books', 'Hlth', 'Carry', 'Coal', 'Oil', 'Util', 'Telcm', 'Servs', 'BusEq', 'Fin', 'Other']\n",
"In-sample OLS R-squared: 7.43%\n",
"---\n",
"LASSO variables selected for Rtail.lead: \n",
"['Rtail']\n",
"Running OLS for Rtail.lead against ['Rtail']\n",
"In-sample OLS R-squared: 1.61%\n",
"---\n",
"LASSO variables selected for Meals.lead: \n",
"['Smoke', 'Books', 'Clths', 'Steel', 'Carry', 'Coal', 'Oil', 'Util', 'Servs', 'BusEq', 'Meals', 'Fin']\n",
"Running OLS for Meals.lead against ['Smoke', 'Books', 'Clths', 'Steel', 'Carry', 'Coal', 'Oil', 'Util', 'Servs', 'BusEq', 'Meals', 'Fin']\n",
"In-sample OLS R-squared: 7.90%\n",
"---\n",
"LASSO variables selected for Fin.lead: \n",
"['Fin']\n",
"Running OLS for Fin.lead against ['Fin']\n",
"In-sample OLS R-squared: 1.70%\n",
"---\n",
"LASSO variables selected for Other.lead: \n",
"['Smoke', 'Clths', 'Coal', 'Oil', 'Util', 'Telcm', 'Servs', 'Trans', 'Fin']\n",
"Running OLS for Other.lead against ['Smoke', 'Clths', 'Coal', 'Oil', 'Util', 'Telcm', 'Servs', 'Trans', 'Fin']\n",
"In-sample OLS R-squared: 4.86%\n",
"---\n",
"[[6, 17, 19, 26], [0, 6, 17], [9, 15, 16, 17, 18, 19, 20, 21, 23, 24, 28], [4, 6, 17, 28], [3, 4, 17, 18, 19, 21, 22, 26, 28], [6, 17, 26], [6, 17, 18, 21, 26], [4, 16, 17, 19], [6], [6, 14, 17, 18, 26, 28], [6, 17, 18, 19, 24, 26, 28], [28], [24, 28], [28], [5, 6, 17, 18, 19, 22, 26, 28], [24], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29], [1, 2, 4, 14, 17, 18, 23, 26], [1, 7, 15], [0, 1, 2, 5, 7, 10, 12, 15, 16, 18, 19, 20, 22, 25, 28, 29], [1, 2, 4, 5, 10, 14, 15, 16, 17, 18, 19, 21, 22, 26, 27, 28], [2, 4, 11, 18, 19, 28], [2, 4, 19], [3, 4, 6, 13, 15, 17, 18, 19, 21, 22, 23, 24, 25, 26, 28], [1, 2, 3, 4, 5, 6, 7, 11, 12, 13, 17, 18, 19, 20, 21, 24, 25, 26, 28, 29], [0, 1, 2, 4, 7, 15, 17, 18, 19, 20, 21, 22, 28, 29], [26], [2, 4, 6, 11, 15, 17, 18, 19, 21, 22, 27, 28], [28], [2, 6, 17, 18, 19, 20, 21, 24, 28]]\n"
]
}
]
},
{
"cell_type": "code",
"metadata": {
"id": "OspHxwFlfkYa",
"outputId": "c59d21eb-5590-4c4f-b713-4e9595e122d4",
"colab": {
"base_uri": "https://localhost:8080/"
}
},
"source": [
"# same predictors selected for all but 2 response vars\n",
"# Differences might be due to small mechanical differences in the mstatistical tools applied.\n",
"# use predictors from paper to match results\n",
"if True: # turn off/on\n",
" coef_dict_temp = {}\n",
" coef_dict_temp['Food.lead'] = ['Clths', 'Coal', 'Util', 'Rtail']\n",
" coef_dict_temp['Beer.lead'] = ['Food', 'Clths', 'Coal']\n",
" coef_dict_temp['Smoke.lead'] = ['Txtls', 'Carry', 'Mines', 'Coal', 'Oil', 'Util', 'Telcm', 'Servs', 'Paper', 'Trans', 'Fin']\n",
" coef_dict_temp['Games.lead'] = ['Books', 'Clths', 'Coal', 'Fin']\n",
" coef_dict_temp['Books.lead'] = ['Games', 'Books', 'Coal', 'Oil', 'Util', 'Servs', 'BusEq', 'Rtail', 'Fin']\n",
" coef_dict_temp['Hshld.lead'] = ['Clths', 'Coal', 'Rtail']\n",
" coef_dict_temp['Clths.lead'] = ['Books', 'Clths', 'Chems', 'Steel', 'ElcEq', 'Carry', 'Coal', 'Oil', 'Util','Telcm', 'Servs', 'BusEq', 'Rtail']\n",
" # Running OLS for Clths against ['Clths', 'Coal', 'Oil', 'Servs', 'Rtail']\n",
" coef_dict_temp['Hlth.lead'] = ['Books', 'Mines', 'Coal', 'Util']\n",
" coef_dict_temp['Chems.lead'] = ['Clths']\n",
" coef_dict_temp['Txtls.lead'] = ['Clths', 'Autos', 'Coal', 'Oil', 'Rtail', 'Fin']\n",
" coef_dict_temp['Cnstr.lead'] = ['Clths', 'Coal', 'Oil', 'Util', 'Trans', 'Rtail', 'Fin']\n",
" coef_dict_temp['Steel.lead'] = ['Fin']\n",
" coef_dict_temp['FabPr.lead'] = ['Trans', 'Fin']\n",
" coef_dict_temp['ElcEq.lead'] = ['Fin']\n",
" coef_dict_temp['Autos.lead'] = ['Hshld', 'Clths', 'Coal', 'Oil', 'Util', 'BusEq', 'Rtail', 'Fin']\n",
" coef_dict_temp['Carry.lead'] = ['Trans']\n",
" coef_dict_temp['Mines.lead'] = []\n",
" coef_dict_temp['Coal.lead'] = ['Beer', 'Smoke', 'Books', 'Autos', 'Coal', 'Oil', 'Paper', 'Rtail']\n",
" coef_dict_temp['Oil.lead'] = ['Beer', 'Hlth', 'Carry']\n",
" coef_dict_temp['Util.lead'] = ['Food', 'Beer', 'Smoke', 'Hshld', 'Hlth', 'Cnstr', 'FabPr', 'Carry', 'Mines', 'Oil', 'Util', 'Telcm', 'BusEq', 'Whlsl', 'Fin', 'Other']\n",
" coef_dict_temp['Telcm.lead'] = ['Beer', 'Smoke', 'Books', 'Hshld', 'Cnstr', 'Autos', 'Carry', 'Mines', 'Coal', 'Oil', 'Util', 'Servs', 'BusEq', 'Rtail', 'Meals', 'Fin']\n",
" coef_dict_temp['Servs.lead'] = ['Smoke', 'Books', 'Steel', 'Oil', 'Util', 'Fin']\n",
" coef_dict_temp['BusEq.lead'] = ['Smoke', 'Books', 'Util']\n",
" coef_dict_temp['Paper.lead'] = ['Clths', 'Coal', 'Oil', 'Rtail', 'Fin']\n",
" coef_dict_temp['Trans.lead'] = ['Fin']\n",
" coef_dict_temp['Whlsl.lead'] = ['Food', 'Beer', 'Smoke', 'Books', 'Hlth', 'Carry', 'Coal', 'Oil', 'Util', 'Telcm', 'Servs', 'BusEq', 'Fin', 'Other']\n",
" # Running OLS for Whlsl against ['Food', 'Smoke', 'Books', 'Carry', 'Coal', 'Oil', 'Util', 'Servs', 'Fin', 'Other']\n",
" coef_dict_temp['Rtail.lead'] = ['Rtail']\n",
" coef_dict_temp['Meals.lead'] = ['Smoke', 'Books', 'Clths', 'Steel', 'Carry', 'Coal', 'Oil', 'Util', 'Servs', 'BusEq', 'Meals', 'Fin']\n",
" coef_dict_temp['Fin.lead'] = ['Fin']\n",
" coef_dict_temp['Other.lead'] = ['Clths', 'Fin']\n",
"\n",
"coef_dict_paper = []\n",
"for response in responses:\n",
" print(response, \" -> \", coef_dict_temp[response])\n",
" coef_dict_paper.append([predictor_reverse_dict[jstr] for jstr in coef_dict_temp[response]])\n",
"print(coef_dict_paper)"
],
"execution_count": null,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Food.lead -> ['Clths', 'Coal', 'Util', 'Rtail']\n",
"Beer.lead -> ['Food', 'Clths', 'Coal']\n",
"Smoke.lead -> ['Txtls', 'Carry', 'Mines', 'Coal', 'Oil', 'Util', 'Telcm', 'Servs', 'Paper', 'Trans', 'Fin']\n",
"Games.lead -> ['Books', 'Clths', 'Coal', 'Fin']\n",
"Books.lead -> ['Games', 'Books', 'Coal', 'Oil', 'Util', 'Servs', 'BusEq', 'Rtail', 'Fin']\n",
"Hshld.lead -> ['Clths', 'Coal', 'Rtail']\n",
"Clths.lead -> ['Books', 'Clths', 'Chems', 'Steel', 'ElcEq', 'Carry', 'Coal', 'Oil', 'Util', 'Telcm', 'Servs', 'BusEq', 'Rtail']\n",
"Hlth.lead -> ['Books', 'Mines', 'Coal', 'Util']\n",
"Chems.lead -> ['Clths']\n",
"Txtls.lead -> ['Clths', 'Autos', 'Coal', 'Oil', 'Rtail', 'Fin']\n",
"Cnstr.lead -> ['Clths', 'Coal', 'Oil', 'Util', 'Trans', 'Rtail', 'Fin']\n",
"Steel.lead -> ['Fin']\n",
"FabPr.lead -> ['Trans', 'Fin']\n",
"ElcEq.lead -> ['Fin']\n",
"Autos.lead -> ['Hshld', 'Clths', 'Coal', 'Oil', 'Util', 'BusEq', 'Rtail', 'Fin']\n",
"Carry.lead -> ['Trans']\n",
"Mines.lead -> []\n",
"Coal.lead -> ['Beer', 'Smoke', 'Books', 'Autos', 'Coal', 'Oil', 'Paper', 'Rtail']\n",
"Oil.lead -> ['Beer', 'Hlth', 'Carry']\n",
"Util.lead -> ['Food', 'Beer', 'Smoke', 'Hshld', 'Hlth', 'Cnstr', 'FabPr', 'Carry', 'Mines', 'Oil', 'Util', 'Telcm', 'BusEq', 'Whlsl', 'Fin', 'Other']\n",
"Telcm.lead -> ['Beer', 'Smoke', 'Books', 'Hshld', 'Cnstr', 'Autos', 'Carry', 'Mines', 'Coal', 'Oil', 'Util', 'Servs', 'BusEq', 'Rtail', 'Meals', 'Fin']\n",
"Servs.lead -> ['Smoke', 'Books', 'Steel', 'Oil', 'Util', 'Fin']\n",
"BusEq.lead -> ['Smoke', 'Books', 'Util']\n",
"Paper.lead -> ['Clths', 'Coal', 'Oil', 'Rtail', 'Fin']\n",
"Trans.lead -> ['Fin']\n",
"Whlsl.lead -> ['Food', 'Beer', 'Smoke', 'Books', 'Hlth', 'Carry', 'Coal', 'Oil', 'Util', 'Telcm', 'Servs', 'BusEq', 'Fin', 'Other']\n",
"Rtail.lead -> ['Rtail']\n",
"Meals.lead -> ['Smoke', 'Books', 'Clths', 'Steel', 'Carry', 'Coal', 'Oil', 'Util', 'Servs', 'BusEq', 'Meals', 'Fin']\n",
"Fin.lead -> ['Fin']\n",
"Other.lead -> ['Clths', 'Fin']\n",
"[[6, 17, 19, 26], [0, 6, 17], [9, 15, 16, 17, 18, 19, 20, 21, 23, 24, 28], [4, 6, 17, 28], [3, 4, 17, 18, 19, 21, 22, 26, 28], [6, 17, 26], [4, 6, 8, 11, 13, 15, 17, 18, 19, 20, 21, 22, 26], [4, 16, 17, 19], [6], [6, 14, 17, 18, 26, 28], [6, 17, 18, 19, 24, 26, 28], [28], [24, 28], [28], [5, 6, 17, 18, 19, 22, 26, 28], [24], [], [1, 2, 4, 14, 17, 18, 23, 26], [1, 7, 15], [0, 1, 2, 5, 7, 10, 12, 15, 16, 18, 19, 20, 22, 25, 28, 29], [1, 2, 4, 5, 10, 14, 15, 16, 17, 18, 19, 21, 22, 26, 27, 28], [2, 4, 11, 18, 19, 28], [2, 4, 19], [6, 17, 18, 26, 28], [28], [0, 1, 2, 4, 7, 15, 17, 18, 19, 20, 21, 22, 28, 29], [26], [2, 4, 6, 11, 15, 17, 18, 19, 21, 22, 27, 28], [28], [6, 28]]\n"
]
}
]
},
{
"cell_type": "code",
"metadata": {
"id": "01Vojt0-fkYe",
"outputId": "0155562a-c6e9-4404-9b54-00feec967cdf",
"colab": {
"base_uri": "https://localhost:8080/"
}
},
"source": [
"# Now just try a simple linear regression on selected features, the idea here is to obtain the same lasso results as the paper\n",
"def predict_with_subsets(X, Y, create_model, coef_dict, verbose=False):\n",
" \"\"\"evaluate subset selection, pass a model function and subsets, compute avg R-squared\"\"\"\n",
" global responses\n",
"\n",
" nrows, ncols = Y.shape\n",
" model = create_model()\n",
"\n",
" scores = []\n",
" for response_col in range(ncols):\n",
" y = Y[:,response_col]\n",
"\n",
"\n",
" if not coef_dict[response_col]:\n",
" if verbose:\n",
" print(\"No coefs selected for \" + responses[response_col])\n",
" # print(\"---\")\n",
" continue\n",
"\n",
" predcols = coef_dict[response_col]\n",
" model.fit(X[:, predcols], y)\n",
" y_pred = model.predict(X[:, predcols])\n",
" score = r2_score(y, y_pred)\n",
" scores.append(score)\n",
" if verbose:\n",
" print (\"In-sample R-squared: %.2f%% for %s against %s\" % (score*100, responses[response_col],\n",
" str([predictors[i] for i in coef_dict[response_col]])))\n",
"# print(\"---\")\n",
"\n",
" if verbose:\n",
" print(\"Mean R-squared: %.2f%%\" % (100 * np.mean(np.array(scores))))\n",
" return np.mean(np.array(scores))\n",
"\n",
"predict_with_subsets(X, Y, LinearRegression, coef_dict_paper, verbose=True)\n"
],
"execution_count": null,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"In-sample R-squared: 2.24% for Food.lead against ['Clths', 'Coal', 'Util', 'Rtail']\n",
"In-sample R-squared: 2.52% for Beer.lead against ['Food', 'Clths', 'Coal']\n",
"In-sample R-squared: 6.55% for Smoke.lead against ['Txtls', 'Carry', 'Mines', 'Coal', 'Oil', 'Util', 'Telcm', 'Servs', 'Paper', 'Trans', 'Fin']\n",
"In-sample R-squared: 5.05% for Games.lead against ['Books', 'Clths', 'Coal', 'Fin']\n",
"In-sample R-squared: 6.30% for Books.lead against ['Games', 'Books', 'Coal', 'Oil', 'Util', 'Servs', 'BusEq', 'Rtail', 'Fin']\n",
"In-sample R-squared: 2.97% for Hshld.lead against ['Clths', 'Coal', 'Rtail']\n",
"In-sample R-squared: 7.82% for Clths.lead against ['Books', 'Clths', 'Chems', 'Steel', 'ElcEq', 'Carry', 'Coal', 'Oil', 'Util', 'Telcm', 'Servs', 'BusEq', 'Rtail']\n",
"In-sample R-squared: 2.68% for Hlth.lead against ['Books', 'Mines', 'Coal', 'Util']\n",
"In-sample R-squared: 0.78% for Chems.lead against ['Clths']\n",
"In-sample R-squared: 7.91% for Txtls.lead against ['Clths', 'Autos', 'Coal', 'Oil', 'Rtail', 'Fin']\n",
"In-sample R-squared: 5.15% for Cnstr.lead against ['Clths', 'Coal', 'Oil', 'Util', 'Trans', 'Rtail', 'Fin']\n",
"In-sample R-squared: 1.28% for Steel.lead against ['Fin']\n",
"In-sample R-squared: 1.57% for FabPr.lead against ['Trans', 'Fin']\n",
"In-sample R-squared: 0.80% for ElcEq.lead against ['Fin']\n",
"In-sample R-squared: 6.13% for Autos.lead against ['Hshld', 'Clths', 'Coal', 'Oil', 'Util', 'BusEq', 'Rtail', 'Fin']\n",
"In-sample R-squared: 2.32% for Carry.lead against ['Trans']\n",
"No coefs selected for Mines.lead\n",
"In-sample R-squared: 2.84% for Coal.lead against ['Beer', 'Smoke', 'Books', 'Autos', 'Coal', 'Oil', 'Paper', 'Rtail']\n",
"In-sample R-squared: 2.52% for Oil.lead against ['Beer', 'Hlth', 'Carry']\n",
"In-sample R-squared: 7.86% for Util.lead against ['Food', 'Beer', 'Smoke', 'Hshld', 'Hlth', 'Cnstr', 'FabPr', 'Carry', 'Mines', 'Oil', 'Util', 'Telcm', 'BusEq', 'Whlsl', 'Fin', 'Other']\n",
"In-sample R-squared: 5.18% for Telcm.lead against ['Beer', 'Smoke', 'Books', 'Hshld', 'Cnstr', 'Autos', 'Carry', 'Mines', 'Coal', 'Oil', 'Util', 'Servs', 'BusEq', 'Rtail', 'Meals', 'Fin']\n",
"In-sample R-squared: 2.87% for Servs.lead against ['Smoke', 'Books', 'Steel', 'Oil', 'Util', 'Fin']\n",
"In-sample R-squared: 2.75% for BusEq.lead against ['Smoke', 'Books', 'Util']\n",
"In-sample R-squared: 3.24% for Paper.lead against ['Clths', 'Coal', 'Oil', 'Rtail', 'Fin']\n",
"In-sample R-squared: 1.32% for Trans.lead against ['Fin']\n",
"In-sample R-squared: 7.43% for Whlsl.lead against ['Food', 'Beer', 'Smoke', 'Books', 'Hlth', 'Carry', 'Coal', 'Oil', 'Util', 'Telcm', 'Servs', 'BusEq', 'Fin', 'Other']\n",
"In-sample R-squared: 1.61% for Rtail.lead against ['Rtail']\n",
"In-sample R-squared: 7.90% for Meals.lead against ['Smoke', 'Books', 'Clths', 'Steel', 'Carry', 'Coal', 'Oil', 'Util', 'Servs', 'BusEq', 'Meals', 'Fin']\n",
"In-sample R-squared: 1.70% for Fin.lead against ['Fin']\n",
"In-sample R-squared: 2.69% for Other.lead against ['Clths', 'Fin']\n",
"Mean R-squared: 3.86%\n"
]
},
{
"output_type": "execute_result",
"data": {
"text/plain": [
"0.03862278631691254"
]
},
"metadata": {},
"execution_count": 14
}
]
},
{
"cell_type": "code",
"metadata": {
"id": "eJqE_OyzfkYj",
"outputId": "4b6e4409-8d7d-4edc-a447-50b04c0431d1",
"colab": {
"base_uri": "https://localhost:8080/"
}
},
"source": [
"# use all predictors - higher in-sample R-squared (almost a universal)\n",
"coef_dict_all = []\n",
"for _ in responses:\n",
" coef_dict_all.append(range(len(predictors)))\n",
"predict_with_subsets(X, Y, LinearRegression, coef_dict_all, verbose=False)\n"
],
"execution_count": null,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"0.06637486888237351"
]
},
"metadata": {},
"execution_count": 15
}
]
},
{
"cell_type": "code",
"metadata": {
"id": "xlRxXibmfkYn",
"outputId": "abf8d4d9-8827-4017-ebe0-c1bb728a5c0d",
"colab": {
"base_uri": "https://localhost:8080/"
}
},
"source": [
"# first iteration will train up to including 196911\n",
"# will use 196912 to predict 197001\n",
"# 1970101 will be first month of performance to use\n",
"# train on first 121 months up to 196912 (0:120), put first prediction in P[121] (122nd row)\n",
"# first month of performance will be 197002\n",
"FIRST_TRAIN_MONTHS = 121\n",
"FIRST_PREDICT_MONTH = FIRST_TRAIN_MONTHS # This is stupid but keeps my head straight\n",
"\n",
"print(X[FIRST_TRAIN_MONTHS])\n",
"print(data.iloc[FIRST_TRAIN_MONTHS][:30])"
],
"execution_count": null,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"[ -3.34 -1.95 -7.59 -7.76 -12.05 -7.5 -5.69 -7.71 -7.37 -5.26\n",
" -9.84 -6.31 -7.15 -6.89 -9.35 -12.49 -2.34 -0.77 -12.16 -4.83\n",
" -3.16 -11.17 -9.73 -8.89 -8.17 -8.28 -6.31 -13.12 -9.78 -6.2 ]\n",
"Food -3.34\n",
"Beer -1.95\n",
"Smoke -7.59\n",
"Games -7.76\n",
"Books -12.05\n",
"Hshld -7.50\n",
"Clths -5.69\n",
"Hlth -7.71\n",
"Chems -7.37\n",
"Txtls -5.26\n",
"Cnstr -9.84\n",
"Steel -6.31\n",
"FabPr -7.15\n",
"ElcEq -6.89\n",
"Autos -9.35\n",
"Carry -12.49\n",
"Mines -2.34\n",
"Coal -0.77\n",
"Oil -12.16\n",
"Util -4.83\n",
"Telcm -3.16\n",
"Servs -11.17\n",
"BusEq -9.73\n",
"Paper -8.89\n",
"Trans -8.17\n",
"Whlsl -8.28\n",
"Rtail -6.31\n",
"Meals -13.12\n",
"Fin -9.78\n",
"Other -6.20\n",
"Name: 197001, dtype: float64\n"
]
}
]
},
{
"cell_type": "code",
"metadata": {
"id": "GnbBd49wfkYt"
},
"source": [
"class PredictWrapper():\n",
" \"\"\"Wrap an sklearn model e.g. LinearRegression to fit, predict all vars as a vector,\n",
" match the way our Keras model will do it\"\"\"\n",
"\n",
" def __init__(self, create_model, coef_dict, fit_missing=None):\n",
" self.create_model = create_model\n",
" self.coef_dict = coef_dict\n",
" self.fit_missing = fit_missing\n",
" self.models = []\n",
"\n",
" def fit(self, X_fit, Y_fit, verbose=False):\n",
"\n",
" self.nrows, self.ycols = Y_fit.shape\n",
" self.models = []\n",
" # fit model for each column\n",
" for responsecol in range(self.ycols):\n",
" if self.coef_dict[responsecol]:\n",
" # column indexes to fit against each other\n",
" predcols = self.coef_dict[responsecol]\n",
" else: # no columns selected\n",
" if not self.fit_missing:\n",
" # default: don't fit if no predictors selected\n",
" self.models.append(None)\n",
" #print(self.models[-1])\n",
" continue\n",
" elif self.fit_missing == \"mean\":\n",
" # append a numeric value to use\n",
" self.models.append(np.mean(Y_fit[:,responsecol]))\n",
" #print(self.models[-1])\n",
" continue\n",
" elif self.fit_missing == \"all\":\n",
" # predcols = all columns\n",
" predcols = range(X_fit.shape[1])\n",
"\n",
" model = self.create_model() # create 1 sklearn model for each column\n",
" model.fit(X_fit[:, predcols], Y_fit[:,responsecol])\n",
" if verbose:\n",
" print(\"fit on \" + str(X_fit[:, predcols].shape) + str(predcols))\n",
" print(model.coef_)\n",
"\n",
" self.models.append(model)\n",
"\n",
" #debug\n",
" #print(responsecol)\n",
" #print(X_fit[:, predcols])\n",
" #print(\"=====\")\n",
" #print(Y_fit[:,responsecol])\n",
" #print(\"=====\")\n",
" #print(self.model.coef_)\n",
" #print(self.model.intercept_)\n",
" #print(\"=====\")\n",
"\n",
" def predict(self, X_predict, verbose=False):\n",
"\n",
" predictions = []\n",
" nrows = X_predict.shape[0]\n",
"\n",
" for responsecol in range(self.ycols):\n",
" #print (type(self.models[responsecol]))\n",
" if type(self.models[responsecol]) == np.float64:\n",
" pred = np.array([self.models[responsecol]] * nrows).reshape(nrows,1)\n",
" predictions.append(pred)\n",
" sys.stdout.write('\\010#')\n",
" elif not self.models[responsecol]:\n",
" # don't predict\n",
" predictions.append(np.array([np.nan] * nrows ).reshape(nrows,1))\n",
" sys.stdout.write('\\010N')\n",
" else:\n",
" predcols = self.coef_dict[responsecol]\n",
" y_pred = self.models[responsecol].predict(X_predict[:,predcols])\n",
" if verbose:\n",
" print(\"predict on\" + str(X_predict[:, predcols].shape) + str(predcols))\n",
" print(y_pred)\n",
" predictions.append(y_pred.reshape(nrows,1))\n",
"\n",
" return np.hstack(predictions)\n",
"\n",
""
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "CnUxMdcdfkYw"
},
"source": [
"### BacktestModel class to abstract walk-forward validation and backtesting workflow from modeling, portfolio construction, reporting"
]
},
{
"cell_type": "code",
"metadata": {
"id": "2yXXF1cUfkYx"
},
"source": [
"# typical pipeline\n",
"# backtestmodel = BacktestModel(X, # predictors\n",
"# Y, # responses\n",
"# create_model=LinearRegression, # create_model which returns a model (needed for 'timestep' which creates different model each timestep)\n",
"# # or model = someKerasModel, # initialized model that supports fit(X,Y), predict(X) , predicts an entire row,\n",
"# coef_dict_param=coef_dict_paper, # how to map predictors to responses (\"all\", \"timestep\", or a list of lists)\n",
"# startindex=FIRST_TRAIN_MONTHS, # initial training for backtest\n",
"# fit_missing='mean', # what to do when no predictors are selected in coef_dict_param - use all predictors, use historical mean, use np.nan\n",
"# scaler = None) # scaling function like MinMaxScaler\n",
"\n",
"# backtestmodel.gen_predictions(verbose=False) # starting from startindex, step through X,Y month by month, fit up to current month, predict next month, store prediction in self.P\n",
"# backtestmodel.walkforward_xval(n_splits=5, verbose=True) # calls gen_predictions with a large step, fits and predicts one large fold at a time (useful to cross-validate quickly)\n",
"# backtestmodel.evaluate_predictions() # report metrics on prediction : MSE etc. #TODO: support custom calcs before/after/instead of default calcs\n",
"# backtestmodel.gen_returns(calc_returns, verbose=True) # takes a function that returns portfolio returns based on self.P, stores in self.R\n",
"# backtestmodel.report_returns(start_date=start_date_str, freq='M') # calc cumulative perf and report (TODO: allow it to take a reporting function to run before/after/in place of default report)\n",
"# backtestmodel.evaluate_quantiles(chart=True, verbose=True) # report quantile metrics # TODO: make this a custom calc passed into evaluate_predictions\n",
"\n",
"class BacktestModel():\n",
"\n",
" def __init__(self,\n",
" X, # predictors\n",
" Y, # responses\n",
" model=None, # model that supports fit(X,Y), predict(X) , predicts an entire row,\n",
" create_model=None, # or create_model which returns a model (needed for 'timestep' but slows down so pass model if dynamic not needed)\n",
" coef_dict_param=\"all\", # mapping of predictors to responses (\"all\", \"timestep\", or a list of lists)\n",
" startindex=FIRST_TRAIN_MONTHS,\n",
" scaler=None,\n",
" fit_missing=None):\n",
"\n",
" self.Xrows, self.Xcols = X.shape\n",
" self.Yrows, self.Ycols = Y.shape\n",
"\n",
" if self.Xrows != self.Yrows:\n",
" raise(ValueError, \"Shapes differ: X %s, Y %s\" % (str(X.shape), str(Y.shape)))\n",
"\n",
" self.X = X\n",
" self.Y = Y\n",
" self.Xscale = X.copy()\n",
" self.Yscale = Y.copy()\n",
"\n",
" if scaler:\n",
" print(\"scaler: %s \" %str(scaler))\n",
" # by rows\n",
" # MinMaxScaler: each row (min->0, max->1)\n",
" # StandardScaler: each row (mean->0, SD->1)\n",
" # self.Xscale = scaler().fit_transform(self.Xscale.transpose()).transpose()\n",
" # self.Yscale = scaler().fit_transform(self.Yscale.transpose()).transpose()\n",
" # by cols\n",
" # MinMaxScaler: each col (min->0, max->1)\n",
" # StandardScaler: each col (mean->0, SD->1)\n",
" self.Xscale = scaler().fit_transform(self.Xscale)\n",
" self.Yscale = scaler().fit_transform(self.Yscale)\n",
"\n",
" self.model = model\n",
" self.create_model = create_model\n",
" self.coef_dict_param = coef_dict_param\n",
" self.startindex = startindex\n",
" self.fit_missing = fit_missing\n",
"\n",
" def fit_predict(self, ntrain, npredict=1, verbose=False):\n",
" \"\"\"for backtest, train model using Y v. X\n",
" train on first ntrain rows. if ntrain=121, fit 0:120\n",
" predict following npredict rows\n",
" if npredict=1, predict row 121\n",
" if npredict=12, predict rows 121-132\n",
" \"\"\"\n",
"\n",
" # fit first ntrain rows\n",
" X_fit = self.Xscale[:ntrain] # e.g. 0:120\n",
" Y_fit = self.Yscale[:ntrain]\n",
" # predict npredict rows\n",
" X_predict = self.Xscale[ntrain:ntrain+npredict] # 121-122\n",
" X_predict = X_predict.reshape(npredict,self.Xcols)\n",
"\n",
" # if no coef_dict select predictors into coef_dict\n",
" if self.coef_dict_param == \"timestep\":\n",
" msg = \"Performing subset selection\"\n",
" coef_dict = subset_selection(X_fit, Y_fit, LassoLarsIC(criterion='aic'))\n",
" # if coef_dict == \"all\" use all predictors for each response\n",
" elif self.coef_dict_param == 'all':\n",
" msg = \"Using all predictors\"\n",
" coef_dict = [range(self.Xcols) for _ in range(self.ycols)]\n",
" else: # should check valid dict\n",
" msg = \"Using coef_dict predictors\"\n",
" coef_dict = self.coef_dict_param\n",
" if verbose:\n",
" print(msg)\n",
"\n",
" if self.create_model:\n",
" self.model = PredictWrapper(self.create_model, coef_dict, fit_missing=self.fit_missing)\n",
"\n",
" self.model.fit(X_fit, Y_fit, verbose=verbose)\n",
" return self.model.predict(X_predict, verbose=verbose)\n",
"\n",
" # predict all months\n",
" # initial train_months = 120 -> train first model on 120 rows\n",
" # first prediction will be in P[120] (121st row)\n",
" # step = 6 -> predict following 6 rows, then step forward 6 months at a time\n",
" # initialize predictions matrix self.P\n",
"\n",
" # use either step or folds\n",
" # step, do range(self.startindex, nrows, step)\n",
" # folds, at each fold train 0:startfold, predict startfold+1:endfold\n",
" # store only out-of-sample predictions in P, calc out-of-sample MSE\n",
"\n",
" # using a step > 1 or folds is quicker, for quicker xval, or to speed up by not estimating model at each timestep\n",
"\n",
" def gen_predictions(self,\n",
" step=1,\n",
" splits=None,\n",
" verbose=False):\n",
"\n",
" self.P = np.zeros_like(self.Y)\n",
"\n",
" progress_i = 0\n",
" self.nrows, self.ycols = Y.shape\n",
"\n",
" if splits:\n",
" month_indexes = splits[:-1] # last index is nrows\n",
" else:\n",
" # create list of steps\n",
" month_indexes = list(range(self.startindex, nrows, step))\n",
" steps = [month_indexes[i+1]-month_indexes[i] for i in range(len(month_indexes)-1)]\n",
" # last step -> end\n",
" steps.append(self.nrows - month_indexes[-1])\n",
"\n",
" if verbose:\n",
" print (\"Steps: \" + str(month_indexes))\n",
"\n",
" for month_index, forecast_rows in zip(month_indexes, steps):\n",
" if verbose:\n",
" print(\"Training on first %d rows (%d:%d), putting predictions in rows %s\" % (month_index,\n",
" 0, month_index-1,\n",
" str(range(month_index,month_index+forecast_rows))))\n",
" predictions = self.fit_predict(month_index, forecast_rows, verbose=False)\n",
"\n",
" first_pred_row = month_index\n",
" for row_index in range(forecast_rows):\n",
" self.P[first_pred_row+row_index] = predictions[row_index]\n",
" sys.stdout.write('.')\n",
" progress_i += 1\n",
" if progress_i % 80 == 0:\n",
" print(\"\")\n",
" print(\"%s Still training step %d of %d\" % (time.strftime(\"%H:%M:%S\"), progress_i, len(month_indexes)))\n",
" sys.stdout.flush()\n",
" print(\"\")\n",
"\n",
"\n",
" def evaluate_predictions(self):\n",
"\n",
" # evaluate prediction (can move to separate function)\n",
" msetemp = (self.P[self.startindex:]-self.Yscale[self.startindex:])**2\n",
" #remove nans\n",
" msetemp = msetemp[~np.isnan(msetemp)]\n",
" self.mse = np.mean(msetemp)\n",
" print(\"OOS MSE across all predictions: %.4f\" % self.mse)\n",
" self.model.fit(self.Xscale, self.Yscale)\n",
" Y_pred = self.model.predict(self.Xscale)\n",
" self.in_sample_mse = np.mean((Y_pred - self.Yscale) ** 2)\n",
" print(\"In-sample MSE: %.4f\" % self.in_sample_mse)\n",
"\n",
" # force unpredicted ys to be nans, then remove nans\n",
" vartemp = self.Yscale[self.startindex:] - self.P[self.startindex:] + self.P[self.startindex:]\n",
" vartemp = vartemp[~np.isnan(vartemp)]\n",
" y_variance = np.var(vartemp[self.startindex:])\n",
" print(\"Variance: %.4f\" % (y_variance))\n",
" print(\"R-squared: %.4f\" % (1- self.mse/y_variance))\n",
"\n",
" return(self.mse)\n",
"\n",
" def evaluate_quantiles(self, chart=False, verbose=False):\n",
"\n",
" self.P_quantiles = np.zeros_like(self.P)\n",
" self.Y_quantiles = np.zeros_like(self.Y)\n",
" self.kendalltaus = []\n",
" self.ktpvals = []\n",
" # compute score for predicted quantiles vs. actual (expected) quantiles\n",
" N_QUANTILES=5\n",
" for row in range(self.startindex, self.P_quantiles.shape[0]):\n",
" #print(self.P[row])\n",
" self.P_quantiles[row] = pd.qcut(self.P[row], N_QUANTILES, range(N_QUANTILES))\n",
" self.Y_quantiles[row] = pd.qcut(self.Y[row], N_QUANTILES, range(N_QUANTILES))\n",
" kt, p_val = kendalltau(self.P[row], self.Y[row])\n",
" self.kendalltaus.append(kt)\n",
" self.ktpvals.append(p_val)\n",
"\n",
" self.kendalltau = np.mean(self.kendalltaus)\n",
" self.kendalltau_pvalue = np.mean(self.ktpvals)\n",
" print(\"Avg rank correlation (Kendall's tau): %.4f (Expected: 0)\" % (self.kendalltau))\n",
" pred_quantiles = self.P_quantiles[self.startindex:]\n",
" true_quantiles = self.Y_quantiles[self.startindex:]\n",
"\n",
" nrows, ncols = pred_quantiles.shape\n",
"\n",
" pred_quantiles = pred_quantiles.reshape(nrows*ncols)\n",
" true_quantiles = true_quantiles.reshape(nrows*ncols)\n",
" self.quintile_accuracy = accuracy_score(pred_quantiles, true_quantiles)\n",
" print(\"5-quintile accuracy: %.4f (Expected: 0.2)\" % (self.quintile_accuracy))\n",
"\n",
" pred_direction = np.zeros(nrows*ncols)\n",
" true_direction = np.zeros(nrows*ncols)\n",
" for i in range(nrows*ncols):\n",
" if pred_quantiles[i] == 4:\n",
" pred_direction[i] = 1\n",
" elif pred_quantiles[i] == 0:\n",
" pred_direction[i] = -1\n",
" if true_quantiles[i] == 4:\n",
" true_direction[i] = 1\n",
" elif true_quantiles[i] == 0:\n",
" true_direction[i] = -1\n",
" self.directional_accuracy = accuracy_score(pred_direction, true_direction)\n",
" print(\"Long/short/flat accuracy: %.4f (Expected: 0.44)\" % (self.directional_accuracy))\n",
"\n",
" nrows = nrows * ncols\n",
"\n",
" conf_mat_expected = np.array([[0.64, 0.16],[0.16, 0.04]])*nrows\n",
"\n",
" myscores = []\n",
" for q in range(5):\n",
" temp_pred = pred_quantiles == q\n",
" temp_actual = true_quantiles == q\n",
" conf_mat5 = confusion_matrix(temp_pred, temp_actual)\n",
" diff_mat = conf_mat5 - conf_mat_expected\n",
" if verbose:\n",
" print(\"Confusion matrix for quantile %d\" % q)\n",
" print(conf_mat5)\n",
" cstmp, cspvtmp = chisquare(conf_mat5.reshape(4), conf_mat_expected.reshape(4))\n",
" print(\"Chi-square: %.4f (p-value: %.8f)\" % (cstmp, cspvtmp))\n",
"\n",
" # probably no valid statistical interpretation but\n",
" # average of improvement in true positive % and true negative %\n",
" myscore = diff_mat[1][1]\n",
" myscores.append(myscore)\n",
"\n",
" # sum of true positive for top and bottom quintiles\n",
" self.excess_tp = myscores[0] + myscores[4]\n",
" print(\"Excess true positive in quintiles 1 + 5: %f\" % (self.excess_tp))\n",
"\n",
" conf_mat = confusion_matrix(pred_quantiles, true_quantiles)\n",
" if chart:\n",
" fig, ax = plt.subplots(figsize=(10,10))\n",
" sns.heatmap(conf_mat, annot=True, fmt='d')\n",
" plt.ylabel('Actual Quintile')\n",
" plt.xlabel('Predicted Quintile')\n",
" plt.show()\n",
"\n",
" return None\n",
"\n",
" def walkforward_xval (self, n_splits=5, verbose=False):\n",
" \"\"\"quick and dirty genreturns, with a step\"\"\"\n",
" # generate k-folds\n",
" kf = KFold(n_splits=n_splits)\n",
" kf.get_n_splits(X)\n",
" last_indexes = []\n",
" for train_index, test_index in kf.split(X):\n",
" # use test_index as last index to train\n",
" last_index = test_index[-1] + 1\n",
" last_indexes.append(last_index)\n",
" print(\"%s Generate splits %s\" % (time.strftime(\"%H:%M:%S\"), str([i for i in last_indexes])))\n",
" #override startindex\n",
" self.startindex = last_indexes[0]\n",
" return self.gen_predictions(splits=last_indexes, verbose=verbose)\n",
"\n",
" def gen_returns(self, port_returns_func, verbose=False):\n",
"\n",
" self.R = np.zeros(self.P.shape[0])\n",
" first_pred_month=self.startindex\n",
"\n",
" indcount = [0] * self.ycols\n",
" longcount = [0] * self.ycols\n",
" shortcount = [0] * self.ycols\n",
"\n",
" for month_index in range(first_pred_month, nrows-1):\n",
" return_month = month_index + 1\n",
" port_return, long_indexes, short_indexes = port_returns_func(self.P[month_index],\n",
" self.X[return_month])\n",
" self.R[return_month] = port_return\n",
"\n",
" for i in long_indexes:\n",
" indcount[i] += 1\n",
" longcount[i] += 1\n",
" for i in short_indexes:\n",
" indcount[i] += 1\n",
" shortcount[i] += 1\n",
" if verbose:\n",
" for i in range(len(responses)):\n",
" print(\"%s: long %d times, short %d times, total %d times\" % (predictors[i],\n",
" longcount[i],\n",
" shortcount[i],\n",
" indcount[i]))\n",
" return self.R\n",
"\n",
" def report_returns(self, start_date='01/01/1970', freq='M'):\n",
"\n",
" first_pred_month=self.startindex\n",
" self.results = self.R[first_pred_month:]\n",
" nmonths = self.results.shape[0]\n",
" nyears = nmonths/12.0\n",
" index = pd.date_range(start_date,periods=nmonths, freq=freq)\n",
" perfdata = pd.DataFrame(self.results,index=index,columns=['Returns'])\n",
" perfdata['Equity'] = 100 * np.cumprod(1 + self.results / 100)\n",
" self.cumulative_return = perfdata['Equity']\n",
" self.mean_monthly_return_annualized = np.mean(1 + self.results/100) ** 12 - 1\n",
" self.mean_return = (self.cumulative_return[-1]/100) ** (1.0/nyears) - 1\n",
" self.annualized_vol = np.std(self.results/100) * np.sqrt(12.0)\n",
" self.sharpe = self.mean_monthly_return_annualized/self.annualized_vol\n",
" print(\"Mean return: %.3f%%\" % (self.mean_return * 100 ))\n",
" #print(\"Mean monthly annualized return: %.3f%%\" % (self.mean_monthly_return_annualized * 100 ))\n",
" #print(\"Monthly annualized volatility: %.3f%%\" % (self.annualized_vol * 100))\n",
" print(\"Monthly Sharpe ratio: %.3f\" % (self.sharpe))\n"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "tdXQ-om1fkY0"
},
"source": [
"# return calculation passed to gen_returns\n",
"# which 6 (40/5) portfolios to go long on and which to go short on.\n",
"\n",
"NUMSTOCKS = 6 # top quintile (and bottom)\n",
"\n",
"def calc_returns(prediction_row, return_row, numstocks=NUMSTOCKS, verbose=False):\n",
"\n",
" # ensure nan sorts to top for shorts\n",
" short_sort_array = [999999 if np.isnan(x) else x for x in prediction_row]\n",
" # pick bottom numstocks\n",
" select_array = np.argsort(short_sort_array)\n",
" short_indexes = select_array[:numstocks]\n",
"\n",
" # ensure nan sorts to bottom for longs\n",
" long_sort_array = [-999999 if np.isnan(x) else x for x in prediction_row]\n",
" # pick top numstocks\n",
" select_array = np.argsort(long_sort_array)\n",
" long_indexes = select_array[-numstocks:]\n",
"\n",
" if verbose:\n",
" print(\"Longs: %s\" %(str([(i,prediction_row[i]) for i in long_indexes])))\n",
" print(\"Shorts: %s\" %(str([(i,prediction_row[i]) for i in short_indexes])))\n",
"\n",
" # compute equal weighted long/short return\n",
" return np.mean(return_row[long_indexes])/2 - np.mean(return_row[short_indexes])/2, long_indexes, short_indexes\n"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "Co4uzjyNfkY3",
"outputId": "4ee485ff-4a50-4530-cb07-2863bc8f6bca",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 35
}
},
"source": [
"start_date_int = data.index[FIRST_TRAIN_MONTHS]\n",
"start_year, start_month = start_date_int // 100, start_date_int % 100\n",
"start_date_str = \"%02d/%02d/%d\" % (start_month, 1, start_year)\n",
"start_date_str"
],
"execution_count": null,
"outputs": [
{
"output_type": "execute_result",
"data": {
"application/vnd.google.colaboratory.intrinsic+json": {
"type": "string"
},
"text/plain": [
"'01/01/1970'"
]
},
"metadata": {},
"execution_count": 20
}
]
},
{
"cell_type": "code",
"metadata": {
"id": "Vu609zIpfkY8",
"outputId": "a0388963-a54a-4496-d491-b2661df18499",
"colab": {
"base_uri": "https://localhost:8080/"
}
},
"source": [
"# test fit_predict\n",
"backtestmodel = BacktestModel(X, Y, create_model=LinearRegression, coef_dict_param=coef_dict_all, startindex=FIRST_TRAIN_MONTHS)\n",
"print(backtestmodel.fit_predict(121, npredict=3, verbose=False))\n"
],
"execution_count": null,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"[[ -4.70219919 -5.3073358 -3.72029191 -12.42448745 -6.62775756\n",
" -2.90316148 -6.79960146 -2.23172984 -5.75101181 -6.66276641\n",
" -5.75265368 -7.71979359 -6.57864566 -4.32124795 -5.0355927\n",
" -5.92230241 -4.76675177 -1.96315925 -3.43369206 -2.30343164\n",
" -3.72794445 -2.91917105 -5.27753906 -6.40474001 -6.85223288\n",
" -10.16508667 -4.69016977 -8.8042513 -5.4481542 -6.40122815]\n",
" [ -1.09819574 0.33751522 1.47276575 -7.65291563 -0.40550754\n",
" -4.96855817 -1.82873168 -3.4663124 -2.05857985 -0.68158685\n",
" -1.54307573 -2.74859589 -2.04412571 -1.77673237 -3.39612781\n",
" -4.76047089 -0.65055072 -5.08513341 1.95082965 -2.60578312\n",
" -5.09137205 -1.72203771 -3.31591182 -1.94755286 -1.38046013\n",
" -2.75841651 -0.96353404 -6.81674253 1.34059086 -1.59466739]\n",
" [ -1.96144894 -0.87682739 -3.02833064 0.58137733 -0.02056567\n",
" 1.67143038 -0.44895783 -1.70395859 -0.63283007 -0.4731534\n",
" -0.71228211 -3.61106494 -1.93572984 -0.64937617 -1.09751999\n",
" 0.24726352 -0.37303973 -1.04141371 2.1679461 -0.55736749\n",
" 0.76975122 0.67561178 1.62692312 -1.12079845 -1.07373008\n",
" -0.27905898 1.55099516 -3.06722504 -0.32196075 0.5859896 ]]\n"
]
}
]
},
{
"cell_type": "code",
"metadata": {
"scrolled": false,
"id": "vmVhPVxyfkZB",
"outputId": "d2553b20-69ac-4858-bf42-a0ea06a57f8e",
"colab": {
"base_uri": "https://localhost:8080/"
}
},
"source": [
"# test model on our calculated coef_dict\n",
"# bad methodology, introduces snooping\n",
"# selects LASSO vars over whole timespan\n",
"# uses selected vars to do OLS backtest\n",
"# included as an example of what not to do\n",
"backtestmodel = BacktestModel(X, Y,\n",
" create_model=LinearRegression,\n",
" coef_dict_param=coef_dict_paper, # Feed paper predictors\n",
" startindex=FIRST_TRAIN_MONTHS,\n",
" fit_missing='mean',\n",
" scaler = None)\n",
"backtestmodel.gen_predictions(verbose=False)\n"
],
"execution_count": null,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\n",
"15:01:39 Still training step 80 of 563\n",
"\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\n",
"15:01:43 Still training step 160 of 563\n",
"\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\n",
"15:01:49 Still training step 240 of 563\n",
"\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\n",
"15:01:54 Still training step 320 of 563\n",
"\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\n",
"15:01:59 Still training step 400 of 563\n",
"\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\n",
"15:02:04 Still training step 480 of 563\n",
"\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\b#.\n",
"15:02:11 Still training step 560 of 563\n",
"\b#.\b#.\b#.\n"
]
}
]
},
{
"cell_type": "code",
"metadata": {
"id": "zvlA7Ea0CqGA",
"outputId": "f438283d-0f25-4892-a8a7-b9a5e1dcb2a5",
"colab": {
"base_uri": "https://localhost:8080/"
}
},
"source": [
"backtestmodel.gen_returns(calc_returns, verbose=True)"
],
"execution_count": null,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Food: long 93 times, short 37 times, total 130 times\n",
"Beer: long 122 times, short 94 times, total 216 times\n",
"Smoke: long 211 times, short 112 times, total 323 times\n",
"Games: long 147 times, short 125 times, total 272 times\n",
"Books: long 126 times, short 107 times, total 233 times\n",
"Hshld: long 81 times, short 72 times, total 153 times\n",
"Clths: long 195 times, short 172 times, total 367 times\n",
"Hlth: long 126 times, short 110 times, total 236 times\n",
"Chems: long 26 times, short 125 times, total 151 times\n",
"Txtls: long 160 times, short 128 times, total 288 times\n",
"Cnstr: long 92 times, short 124 times, total 216 times\n",
"Steel: long 4 times, short 180 times, total 184 times\n",
"FabPr: long 33 times, short 62 times, total 95 times\n",
"ElcEq: long 45 times, short 16 times, total 61 times\n",
"Autos: long 87 times, short 117 times, total 204 times\n",
"Carry: long 106 times, short 76 times, total 182 times\n",
"Mines: long 132 times, short 102 times, total 234 times\n",
"Coal: long 226 times, short 183 times, total 409 times\n",
"Oil: long 160 times, short 137 times, total 297 times\n",
"Util: long 161 times, short 182 times, total 343 times\n",
"Telcm: long 118 times, short 163 times, total 281 times\n",
"Servs: long 149 times, short 137 times, total 286 times\n",
"BusEq: long 106 times, short 123 times, total 229 times\n",
"Paper: long 50 times, short 107 times, total 157 times\n",
"Trans: long 39 times, short 56 times, total 95 times\n",
"Whlsl: long 168 times, short 147 times, total 315 times\n",
"Rtail: long 72 times, short 62 times, total 134 times\n",
"Meals: long 220 times, short 178 times, total 398 times\n",
"Fin: long 56 times, short 21 times, total 77 times\n",
"Other: long 61 times, short 117 times, total 178 times\n"
]
},
{
"output_type": "execute_result",
"data": {
"text/plain": [
"array([ 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n",
" 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n",
" 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n",
" 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n",
" 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n",
" 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n",
" 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n",
" 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n",
" 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n",
" 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n",
" 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n",
" 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n",
" 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n",
" 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n",
" 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n",
" 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n",
" 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n",
" 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n",
" 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n",
" 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n",
" 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n",
" 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n",
" 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n",
" 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n",
" 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n",
" 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n",
" 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n",
" 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n",
" 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n",
" 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n",
" 0.00000000e+00, 0.00000000e+00, 6.60000000e-01, -8.75000000e-01,\n",
" 4.76333333e+00, 3.80833333e+00, 2.68166667e+00, -8.15000000e-01,\n",
" 1.34583333e+00, 2.10583333e+00, -1.06666667e+00, 9.33333333e-02,\n",
" -1.12333333e+00, 2.42333333e+00, 1.16000000e+00, 1.71250000e+00,\n",
" -1.41000000e+00, 6.67500000e-01, 1.54583333e+00, 9.75000000e-01,\n",
" -2.19000000e+00, 6.77500000e-01, 7.33333333e-02, 5.50000000e-01,\n",
" -1.61083333e+00, 1.90666667e+00, -1.94166667e-01, 8.91666667e-02,\n",
" 1.16416667e+00, 5.38333333e-01, 2.58333333e-01, -1.40000000e-01,\n",
" 2.24000000e+00, -9.40000000e-01, 1.36750000e+00, 1.05416667e+00,\n",
" -1.14083333e+00, -1.19750000e+00, 3.91916667e+00, 1.20333333e+00,\n",
" 2.25000000e+00, 1.79666667e+00, 3.38333333e+00, -2.58583333e+00,\n",
" 1.07500000e-01, 1.46750000e+00, 1.60166667e+00, 5.13666667e+00,\n",
" 4.07583333e+00, -2.61750000e+00, 6.66666667e-01, -1.68500000e+00,\n",
" 1.14916667e+00, 9.25000000e-02, -2.63333333e-01, 2.40833333e+00,\n",
" 1.66333333e+00, 1.81666667e+00, 6.46250000e+00, 1.04333333e+00,\n",
" 2.64500000e+00, -7.86333333e+00, 2.74666667e+00, 8.89166667e-01,\n",
" 1.79333333e+00, 3.28750000e+00, -3.71666667e-01, 1.53333333e-01,\n",
" 1.71666667e-01, 7.40833333e-01, -2.32083333e+00, 1.68500000e+00,\n",
" -1.31000000e+00, -9.46666667e-01, 1.10166667e+00, -4.05000000e-01,\n",
" -2.22250000e+00, 2.47500000e+00, 9.20833333e-01, 4.56666667e-01,\n",
" 5.08333333e-01, 3.05833333e-01, -6.41666667e-02, 1.83750000e+00,\n",
" -3.59166667e-01, 3.75000000e-02, 2.98333333e-01, 1.87500000e-01,\n",
" -2.50000000e-01, 2.06666667e-01, 8.08333333e-01, 1.51750000e+00,\n",
" 1.11416667e+00, -3.50000000e-01, 2.49166667e-01, -1.00750000e+00,\n",
" 8.95000000e-01, -4.27500000e-01, 1.54166667e-01, -2.63333333e-01,\n",
" 7.28333333e-01, 9.93333333e-01, 8.44166667e-01, -9.31666667e-01,\n",
" 1.92333333e+00, -1.17250000e+00, 2.12416667e+00, -6.11666667e-01,\n",
" 5.02500000e-01, 8.18333333e-01, 2.71666667e-01, 7.45833333e-01,\n",
" -4.00833333e-01, -7.36666667e-01, 1.74250000e+00, 1.66250000e+00,\n",
" 1.16166667e+00, -2.36666667e-01, -6.58333333e-01, 1.76000000e+00,\n",
" -4.64166667e-01, 4.73000000e+00, 1.26916667e+00, -2.37583333e+00,\n",
" 3.05583333e+00, -1.76583333e+00, -3.77500000e-01, 2.62500000e+00,\n",
" 9.06666667e-01, 1.50083333e+00, 6.71666667e-01, 4.01416667e+00,\n",
" -5.13333333e-01, -3.23333333e-01, 1.66916667e+00, 1.66333333e+00,\n",
" 2.60833333e-01, 5.07500000e-01, -5.68333333e-01, 2.74083333e+00,\n",
" 5.92500000e-01, -9.00000000e-02, 2.11666667e-01, -1.19916667e+00,\n",
" 7.90833333e-01, 8.24166667e-01, 1.14000000e+00, 1.28166667e+00,\n",
" 1.32750000e+00, 4.60000000e-01, 5.00000000e-03, 1.55166667e+00,\n",
" -4.04166667e-01, 1.83500000e+00, 2.47583333e+00, 2.69916667e+00,\n",
" -2.95000000e+00, 1.26083333e+00, -2.27000000e+00, -2.30000000e-01,\n",
" 6.33333333e-01, 9.92500000e-01, -4.57500000e-01, -1.52750000e+00,\n",
" 2.76000000e+00, -1.01666667e+00, 5.05833333e-01, -9.43333333e-01,\n",
" -1.00833333e-01, 3.22833333e+00, 1.53583333e+00, -5.95833333e-01,\n",
" -3.65833333e-01, 7.60833333e-01, -2.00000000e-02, 6.33333333e-02,\n",
" -1.30583333e+00, -7.70833333e-01, 9.77500000e-01, 3.77500000e-01,\n",
" -2.04166667e-01, -2.41666667e-02, -5.10000000e-01, 3.15166667e+00,\n",
" 7.45833333e-01, -5.10000000e-01, 1.25000000e+00, -1.76000000e+00,\n",
" 4.62500000e-01, -5.05833333e-01, -8.15833333e-01, 1.80166667e+00,\n",
" 1.79583333e+00, 3.25833333e-01, 3.08333333e+00, 9.95000000e-01,\n",
" 8.03333333e-01, 8.91666667e-02, 1.28916667e+00, 1.04833333e+00,\n",
" 8.80833333e-01, 1.54000000e+00, -1.99166667e+00, 1.64166667e-01,\n",
" -5.48333333e-01, -5.75000000e-01, 3.62416667e+00, -2.28083333e+00,\n",
" 1.19333333e+00, 3.77500000e-01, -5.95000000e-01, -2.85583333e+00,\n",
" -1.10416667e+00, 1.07916667e+00, 7.53333333e-01, 9.65833333e-01,\n",
" -1.23666667e+00, -8.00833333e-01, -1.40750000e+00, 5.86666667e-01,\n",
" 1.03500000e+00, 7.19166667e-01, -1.01750000e+00, -6.96666667e-01,\n",
" 1.05083333e+00, -2.45833333e-01, -4.28333333e-01, 6.88333333e-01,\n",
" -1.47166667e+00, 1.19166667e-01, -2.18333333e-01, 6.41666667e-02,\n",
" 8.50000000e-01, 1.24833333e+00, 1.79833333e+00, -3.89166667e-01,\n",
" -1.37166667e+00, -1.21916667e+00, 2.30833333e-01, 1.06500000e+00,\n",
" -1.01666667e-01, 2.39500000e+00, 4.45000000e-01, 4.85000000e-01,\n",
" 1.17083333e+00, -1.14333333e+00, 1.78083333e+00, 9.57500000e-01,\n",
" 1.71250000e+00, 4.44500000e+00, 8.21666667e-01, 1.37333333e+00,\n",
" 9.40833333e-01, 1.12833333e+00, 1.32750000e+00, 2.48333333e-01,\n",
" -1.73583333e+00, -1.31000000e+00, -1.19916667e+00, -7.10833333e-01,\n",
" 1.77916667e+00, 2.21666667e-01, -1.11250000e+00, -1.24666667e+00,\n",
" 1.32416667e+00, 3.96083333e+00, 6.97500000e-01, 5.29166667e-01,\n",
" 1.21666667e-01, 6.04166667e-01, 1.73500000e+00, -5.50000000e-01,\n",
" 7.73333333e-01, 1.66666667e-02, -2.19166667e-01, 2.89000000e+00,\n",
" -6.23333333e-01, 1.00916667e+00, 5.84166667e-01, -2.25000000e-02,\n",
" -9.71666667e-01, 1.54250000e+00, 1.30000000e-01, 1.87500000e-01,\n",
" -2.20833333e-01, 3.88333333e-01, 2.68333333e-01, 1.48333333e-01,\n",
" -8.49166667e-01, -5.95000000e-01, -2.10000000e-01, -3.84166667e-01,\n",
" 6.09166667e-01, -8.55833333e-01, -6.41666667e-02, 3.49166667e-01,\n",
" -3.18333333e-01, -4.62500000e-01, -1.66666667e+00, 1.61666667e+00,\n",
" -1.25500000e+00, 1.48000000e+00, 3.24166667e-01, -1.19333333e+00,\n",
" 7.41666667e-02, 7.85000000e-01, -6.83333333e-02, -1.16666667e+00,\n",
" -1.46583333e+00, -6.95833333e-01, 2.95416667e+00, 6.43333333e-01,\n",
" -2.35416667e+00, 1.31333333e+00, 8.03333333e-01, 1.71250000e+00,\n",
" 1.86666667e-01, 1.53500000e+00, -6.60833333e-01, 3.54166667e-01,\n",
" -1.95083333e+00, 1.81500000e+00, -6.46666667e-01, 1.58583333e+00,\n",
" -1.54833333e+00, -1.86666667e-01, 1.42416667e+00, -7.13333333e-01,\n",
" 8.98333333e-01, 1.59833333e+00, 6.43333333e-01, 2.95833333e-01,\n",
" 1.62500000e+00, -3.67500000e-01, 1.79333333e+00, 3.00000000e+00,\n",
" 1.75416667e+00, 1.71166667e+00, 5.79166667e-01, -7.65833333e-01,\n",
" 1.09000000e+00, -5.67500000e-01, 2.22333333e+00, 1.18333333e+00,\n",
" 2.51000000e+00, 2.92500000e+00, 1.82166667e+00, 1.77083333e+00,\n",
" 4.38083333e+00, 4.69583333e+00, -1.97083333e+00, 3.80166667e+00,\n",
" -6.34166667e-01, -8.08333333e-01, -1.31666667e-01, 3.34166667e-01,\n",
" 2.87916667e+00, 7.60833333e-01, 4.09000000e+00, 2.05333333e+00,\n",
" -4.91666667e-02, 7.51916667e+00, 6.36583333e+00, -2.30583333e+00,\n",
" 2.74000000e+00, 6.76666667e+00, -7.55000000e-01, -4.43333333e-01,\n",
" 3.37166667e+00, 2.03750000e+00, 3.15416667e+00, 3.41833333e+00,\n",
" 2.74916667e+00, 7.64166667e-01, -1.89166667e-01, 2.48833333e+00,\n",
" 2.22500000e+00, -1.16333333e+00, 2.29333333e+00, 4.00750000e+00,\n",
" 3.94166667e+00, 2.69500000e+00, -2.14583333e+00, -1.91000000e+00,\n",
" 1.30083333e+00, -5.90833333e-01, 4.39750000e+00, -1.15416667e+00,\n",
" -2.04833333e+00, 5.81666667e-01, 3.13000000e+00, 7.50000000e-01,\n",
" 8.96666667e-01, 4.72500000e-01, 1.92916667e+00, 7.29166667e-01,\n",
" -4.98333333e-01, -1.07083333e+00, 9.63333333e-01, -1.66416667e+00,\n",
" -1.02750000e+00, -2.19583333e+00, 7.95000000e-01, -1.78833333e+00,\n",
" 4.95000000e-01, -6.09166667e-01, 2.73333333e-01, 2.92500000e-01,\n",
" -9.83333333e-01, 1.38833333e+00, 2.05000000e-01, -4.50000000e-02,\n",
" -2.71666667e-01, -2.16666667e-02, 1.23916667e+00, 1.28000000e+00,\n",
" 2.95000000e-01, -1.08750000e+00, -9.63333333e-01, 2.34166667e-01,\n",
" -1.00583333e+00, -2.16666667e-01, 3.71416667e+00, 3.04166667e-01,\n",
" 1.44333333e+00, 1.15000000e-01, 6.65000000e-01, 3.73333333e-01,\n",
" 9.60000000e-01, 2.93916667e+00, -1.05416667e+00, 3.58333333e-01,\n",
" 1.08333333e-02, 1.14000000e+00, -2.86666667e-01, -2.33000000e+00,\n",
" 2.64833333e+00, 6.33333333e-02, 7.77500000e-01, 3.79250000e+00,\n",
" 1.35416667e+00, 1.78833333e+00, -7.75000000e-01, 6.62500000e-01,\n",
" 7.00000000e-02, 1.06583333e+00, 4.80833333e-01, 1.31666667e-01,\n",
" -2.77500000e-01, 2.85000000e-01, 1.17416667e+00, 1.37500000e+00,\n",
" 1.05000000e+00, -1.67083333e+00, -1.37250000e+00, -1.90250000e+00,\n",
" 2.39166667e+00, -2.74166667e+00, 3.90833333e-01, -4.33333333e-01,\n",
" -2.97583333e+00, 6.60000000e-01, 4.77750000e+00, -2.47750000e+00,\n",
" 2.75833333e+00, 4.04583333e+00, 2.25833333e-01, 3.00583333e+00,\n",
" 6.80000000e-01, -3.28333333e-01, 4.85750000e+00, -2.07166667e+00,\n",
" 1.30683333e+01, 4.25833333e-01, -1.15500000e+00, -7.86666667e-01,\n",
" 9.09166667e-01, 4.15833333e-01, 5.75000000e-02, 1.37916667e+00,\n",
" 2.76916667e+00, 4.57500000e-01, -5.66666667e-02, -9.73333333e-01,\n",
" 2.81666667e+00, -7.67500000e-01, 3.27083333e+00, -1.52166667e+00,\n",
" -8.75833333e-01, -1.18000000e+00, 2.89166667e-01, 2.40083333e+00,\n",
" 1.03916667e+00, 1.99416667e+00, -5.55000000e-01, -3.72500000e-01,\n",
" -9.40833333e-01, 2.56916667e+00, 2.91666667e-02, 1.96250000e+00,\n",
" 2.88250000e+00, 6.37083333e+00, -4.72666667e+00, -6.77500000e-01,\n",
" 2.19666667e+00, -1.38333333e-01, 4.23333333e-01, 1.52833333e+00,\n",
" -5.43333333e-01, -1.03750000e+00, -1.19750000e+00, 3.70833333e-01,\n",
" -9.00000000e-02, 2.33333333e-01, 1.13750000e+00, -1.38833333e+00,\n",
" -1.11666667e+00, -1.69166667e-01, 1.27416667e+00, 7.15000000e-01,\n",
" 2.51666667e+00, -2.21666667e+00, -2.63750000e+00, -3.15833333e-01,\n",
" 6.12500000e-01, -1.19916667e+00, -2.45833333e-01, -3.55000000e-01,\n",
" -6.21666667e-01, 9.66666667e-01, -1.81666667e-01, -1.12833333e+00,\n",
" 2.47500000e-01, -4.89166667e-01, -1.55000000e-01, 4.10833333e-01,\n",
" -3.64166667e-01, 2.38833333e+00, 1.02666667e+00, 1.27583333e+00,\n",
" -2.60000000e-01, 2.06500000e+00, -4.89166667e-01, -1.17333333e+00,\n",
" -3.01666667e-01, 4.90000000e-01, 2.56416667e+00, 4.78583333e+00,\n",
" -1.27916667e+00, -2.12916667e+00, 2.23583333e+00, -4.37500000e-01,\n",
" -1.16666667e-01, 3.02083333e+00, -2.29583333e+00, 9.46666667e-01,\n",
" 3.38666667e+00, -6.16666667e-02, -1.62333333e+00, -3.78250000e+00,\n",
" 3.65833333e-01, -5.03333333e-01, 5.98333333e-01, -1.39000000e+00])"
]
},
"metadata": {},
"execution_count": 23
}
]
},
{
"cell_type": "code",
"metadata": {
"id": "4YGCSmw9CyAc",
"outputId": "e3f4306a-a859-403c-8379-baa9bc3d3d8d",
"colab": {
"base_uri": "https://localhost:8080/"
}
},
"source": [
"backtestmodel.report_returns(start_date=start_date_str, freq='M')"
],
"execution_count": null,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Mean return: 6.667%\n",
"Monthly Sharpe ratio: 1.123\n"
]
}
]
},
{
"cell_type": "code",
"metadata": {
"id": "zyoWNsP_C1iO",
"outputId": "b41b7806-e0d8-4100-ffd1-985d97486ba4",
"colab": {
"base_uri": "https://localhost:8080/"
}
},
"source": [
"backtestmodel.evaluate_predictions()"
],
"execution_count": null,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"OOS MSE across all predictions: 39.4986\n",
"\b#In-sample MSE: 35.8279\n",
"Variance: 39.4097\n",
"R-squared: -0.0023\n"
]
},
{
"output_type": "execute_result",
"data": {
"text/plain": [
"39.49859309389331"
]
},
"metadata": {},
"execution_count": 25
}
]
},
{
"cell_type": "code",
"metadata": {
"id": "8YDisSj-X_b9",
"outputId": "100eda11-7716-4711-aacb-ff37741c240d",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 1000
}
},
"source": [
"\n",
"backtestmodel.evaluate_quantiles(chart=True, verbose=True)\n",
"\n",
"## There is clearly some signal here."
],
"execution_count": null,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Avg rank correlation (Kendall's tau): 0.0589 (Expected: 0)\n",
"5-quintile accuracy: 0.2287 (Expected: 0.2)\n",
"Long/short/flat accuracy: 0.4706 (Expected: 0.44)\n",
"Confusion matrix for quantile 0\n",
"[[10953 2559]\n",
" [ 2555 823]]\n",
"Chi-square: 49.7107 (p-value: 0.00000000)\n",
"Confusion matrix for quantile 1\n",
"[[10884 2628]\n",
" [ 2628 750]]\n",
"Chi-square: 12.8020 (p-value: 0.00508507)\n",
"Confusion matrix for quantile 2\n",
"[[10850 2662]\n",
" [ 2656 722]]\n",
"Chi-square: 4.7384 (p-value: 0.19198761)\n",
"Confusion matrix for quantile 3\n",
"[[10851 2661]\n",
" [ 2666 712]]\n",
"Chi-square: 3.2442 (p-value: 0.35547827)\n",
"Confusion matrix for quantile 4\n",
"[[10994 2518]\n",
" [ 2523 855]]\n",
"Chi-square: 75.2761 (p-value: 0.00000000)\n",
"Excess true positive in quintiles 1 + 5: 326.800000\n"
]
},
{
"output_type": "display_data",
"data": {
"image/png": "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\n",
"text/plain": [
"<Figure size 720x720 with 2 Axes>"
]
},
"metadata": {
"needs_background": "light"
}
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "PrKpbS8XfkZE"
},
"source": [
"### Functions using Plotly to generate charts for performance, scatters, heatmaps"
]
},
{
"cell_type": "code",
"metadata": {
"id": "8eAe0A8KfkZM"
},
"source": [
"# chart performance\n",
"\n",
"def mychart(args, names=None, title=\"\"):\n",
" x_coords = np.linspace(1970, 2016, args[0].shape[0])\n",
" plotdata = []\n",
" for i in range(len(args)):\n",
" tracelabel = \"Trace %d\" % i\n",
" if names:\n",
" tracelabel=names[i]\n",
" plotdata.append(Scatter(x=x_coords,\n",
" y=args[i].values.reshape(-1),\n",
" mode = 'line',\n",
" name=tracelabel))\n",
"\n",
" layout = Layout(\n",
" title = title,\n",
" autosize=False,\n",
" width=900,\n",
" height=600,\n",
" yaxis=dict(\n",
" type='log',\n",
" autorange=True\n",
" )\n",
" )\n",
"\n",
" fig = Figure(data=plotdata, layout=layout)\n",
"\n",
" return iplot(fig)\n",
"\n"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "m5l3PH6SfkZP"
},
"source": [
"def myscatter(arg1, arg2, names=None, title=\"\"):\n",
"\n",
" plotdata = []\n",
"\n",
" plotdata.append(Scatter(\n",
" x = arg1,\n",
" y = arg2,\n",
" mode = 'markers'\n",
" ))\n",
"\n",
" layout = dict(\n",
" title=title,\n",
" autosize=False,\n",
" width=\"600\",\n",
" height=\"480\",\n",
" yaxis=dict(\n",
"# type='log',\n",
" autorange=True\n",
" )\n",
" )\n",
"\n",
"# py.iplot(data, filename='basic-scatter')\n",
"\n",
" fig = Figure(data=plotdata, layout=layout)\n",
"\n",
" return iplot(fig)"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "kTX6zsSwfkZT"
},
"source": [
"def plot_matrix(lossframe, x_labels, y_labels, x_suffix=\"\", y_suffix=\"\"):\n",
"\n",
" pivot = lossframe.pivot_table(index=[y_labels], columns=[x_labels], values=['mse'])\n",
"# print(pivot)\n",
" # specify labels as strings, to force plotly to use a discrete axis\n",
"# print(pivot.columns.levels[1]).values\n",
"# print(lossframe[x_labels].dtype)\n",
"\n",
" if lossframe[x_labels].dtype == np.float64 or lossframe[x_labels].dtype == np.float32:\n",
" xaxis = [\"%f %s\" % (i, x_suffix) for i in pivot.columns.levels[1].values]\n",
" else:\n",
" xaxis = [\"%d %s\" % (i, x_suffix) for i in pivot.columns.levels[1].values]\n",
" if lossframe[y_labels].dtype == np.float64 or lossframe[y_labels].dtype == np.float32:\n",
" yaxis = [\"%f %s\" % (i, y_suffix) for i in pivot.index.values]\n",
" else:\n",
" yaxis = [\"%d %s\" % (i, y_suffix) for i in pivot.index.values]\n",
"\n",
"# print(xaxis, yaxis)\n",
" \"\"\"plot a heat map of a matrix\"\"\"\n",
" chart_width=640\n",
" chart_height=480\n",
"\n",
" layout = dict(\n",
" title=\"%s v. %s\" % (x_labels, y_labels),\n",
" height=chart_height,\n",
" width=chart_width,\n",
" margin=dict(\n",
" l=150,\n",
" r=30,\n",
" b=120,\n",
" t=100,\n",
" ),\n",
" xaxis=dict(\n",
" title=x_labels,\n",
" tickfont=dict(\n",
" family='Arial, sans-serif',\n",
" size=10,\n",
" color='black'\n",
" ),\n",
" ),\n",
" yaxis=dict(\n",
" title=y_labels,\n",
" tickfont=dict(\n",
" family='Arial, sans-serif',\n",
" size=10,\n",
" color='black'\n",
" ),\n",
" ),\n",
" )\n",
"\n",
" data = [Heatmap(z=pivot.values,\n",
" x=xaxis,\n",
" y=yaxis,\n",
" colorscale=[[0, 'rgb(0,0,255)', [1, 'rgb(255,0,0)']]],\n",
" )\n",
" ]\n",
"\n",
" fig = Figure(data=data, layout=layout)\n",
" return iplot(fig, link_text=\"\")"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "39PVdCojfkZW"
},
"source": [
"### Compare LASSO, OLS models"
]
},
{
"cell_type": "code",
"metadata": {
"id": "C5UHu6uEb-gF"
},
"source": [
"def configure_plotly_browser_state():\n",
" import IPython\n",
" display(IPython.core.display.HTML('''\n",
" <script src=\"/static/components/requirejs/require.js\"></script>\n",
" <script>\n",
" requirejs.config({\n",
" paths: {\n",
" base: '/static/base',\n",
" plotly: 'https://cdn.plot.ly/plotly-latest.min.js?noext',\n",
" },\n",
" });\n",
" </script>\n",
" '''))"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "gWUefasJfkZX",
"outputId": "d19705d1-b9e3-4b81-f15c-078f193a78a2",
"colab": {
"resources": {
"http://localhost:8080/static/components/requirejs/require.js": {
"data": "LyoqIHZpbTogZXQ6dHM9NDpzdz00OnN0cz00CiAqIEBsaWNlbnNlIFJlcXVpcmVKUyAyLjEuMjIgQ29weXJpZ2h0IChjKSAyMDEwLTIwMTUsIFRoZSBEb2pvIEZvdW5kYXRpb24gQWxsIFJpZ2h0cyBSZXNlcnZlZC4KICogQXZhaWxhYmxlIHZpYSB0aGUgTUlUIG9yIG5ldyBCU0QgbGljZW5zZS4KICogc2VlOiBodHRwOi8vZ2l0aHViLmNvbS9qcmJ1cmtlL3JlcXVpcmVqcyBmb3IgZGV0YWlscwogKi8KLy9Ob3QgdXNpbmcgc3RyaWN0OiB1bmV2ZW4gc3RyaWN0IHN1cHBvcnQgaW4gYnJvd3NlcnMsICMzOTIsIGFuZCBjYXVzZXMKLy9wcm9ibGVtcyB3aXRoIHJlcXVpcmVqcy5leGVjKCkvdHJhbnNwaWxlciBwbHVnaW5zIHRoYXQgbWF5IG5vdCBiZSBzdHJpY3QuCi8qanNsaW50IHJlZ2V4cDogdHJ1ZSwgbm9tZW46IHRydWUsIHNsb3BweTogdHJ1ZSAqLwovKmdsb2JhbCB3aW5kb3csIG5hdmlnYXRvciwgZG9jdW1lbnQsIGltcG9ydFNjcmlwdHMsIHNldFRpbWVvdXQsIG9wZXJhICovCgp2YXIgcmVxdWlyZWpzLCByZXF1aXJlLCBkZWZpbmU7CihmdW5jdGlvbiAoZ2xvYmFsKSB7CiAgICB2YXIgcmVxLCBzLCBoZWFkLCBiYXNlRWxlbWVudCwgZGF0YU1haW4sIHNyYywKICAgICAgICBpbnRlcmFjdGl2ZVNjcmlwdCwgY3VycmVudGx5QWRkaW5nU2NyaXB0LCBtYWluU2NyaXB0LCBzdWJQYXRoLAogICAgICAgIHZlcnNpb24gPSAnMi4xLjIyJywKICAgICAgICBjb21tZW50UmVnRXhwID0gLyhcL1wqKFtcc1xTXSo/KVwqXC98KFteOl18XilcL1wvKC4qKSQpL21nLAogICAgICAgIGNqc1JlcXVpcmVSZWdFeHAgPSAvW14uXVxzKnJlcXVpcmVccypcKFxzKlsiJ10oW14nIlxzXSspWyInXVxzKlwpL2csCiAgICAgICAganNTdWZmaXhSZWdFeHAgPSAvXC5qcyQvLAogICAgICAgIGN1cnJEaXJSZWdFeHAgPSAvXlwuXC8vLAogICAgICAgIG9wID0gT2JqZWN0LnByb3RvdHlwZSwKICAgICAgICBvc3RyaW5nID0gb3AudG9TdHJpbmcsCiAgICAgICAgaGFzT3duID0gb3AuaGFzT3duUHJvcGVydHksCiAgICAgICAgYXAgPSBBcnJheS5wcm90b3R5cGUsCiAgICAgICAgaXNCcm93c2VyID0gISEodHlwZW9mIHdpbmRvdyAhPT0gJ3VuZGVmaW5lZCcgJiYgdHlwZW9mIG5hdmlnYXRvciAhPT0gJ3VuZGVmaW5lZCcgJiYgd2luZG93LmRvY3VtZW50KSwKICAgICAgICBpc1dlYldvcmtlciA9ICFpc0Jyb3dzZXIgJiYgdHlwZW9mIGltcG9ydFNjcmlwdHMgIT09ICd1bmRlZmluZWQnLAogICAgICAgIC8vUFMzIGluZGljYXRlcyBsb2FkZWQgYW5kIGNvbXBsZXRlLCBidXQgbmVlZCB0byB3YWl0IGZvciBjb21wbGV0ZQogICAgICAgIC8vc3BlY2lmaWNhbGx5LiBTZXF1ZW5jZSBpcyAnbG9hZGluZycsICdsb2FkZWQnLCBleGVjdXRpb24sCiAgICAgICAgLy8gdGhlbiAnY29tcGxldGUnLiBUaGUgVUEgY2hlY2sgaXMgdW5mb3J0dW5hdGUsIGJ1dCBub3Qgc3VyZSBob3cKICAgICAgICAvL3RvIGZlYXR1cmUgdGVzdCB3L28gY2F1c2luZyBwZXJmIGlzc3Vlcy4KICAgICAgICByZWFkeVJlZ0V4cCA9IGlzQnJvd3NlciAmJiBuYXZpZ2F0b3IucGxhdGZvcm0gPT09ICdQTEFZU1RBVElPTiAzJyA/CiAgICAgICAgICAgICAgICAgICAgICAvXmNvbXBsZXRlJC8gOiAvXihjb21wbGV0ZXxsb2FkZWQpJC8sCiAgICAgICAgZGVmQ29udGV4dE5hbWUgPSAnXycsCiAgICAgICAgLy9PaCB0aGUgdHJhZ2VkeSwgZGV0ZWN0aW5nIG9wZXJhLiBTZWUgdGhlIHVzYWdlIG9mIGlzT3BlcmEgZm9yIHJlYXNvbi4KICAgICAgICBpc09wZXJhID0gdHlwZW9mIG9wZXJhICE9PSAndW5kZWZpbmVkJyAmJiBvcGVyYS50b1N0cmluZygpID09PSAnW29iamVjdCBPcGVyYV0nLAogICAgICAgIGNvbnRleHRzID0ge30sCiAgICAgICAgY2ZnID0ge30sCiAgICAgICAgZ2xvYmFsRGVmUXVldWUgPSBbXSwKICAgICAgICB1c2VJbnRlcmFjdGl2ZSA9IGZhbHNlOwoKICAgIGZ1bmN0aW9uIGlzRnVuY3Rpb24oaXQpIHsKICAgICAgICByZXR1cm4gb3N0cmluZy5jYWxsKGl0KSA9PT0gJ1tvYmplY3QgRnVuY3Rpb25dJzsKICAgIH0KCiAgICBmdW5jdGlvbiBpc0FycmF5KGl0KSB7CiAgICAgICAgcmV0dXJuIG9zdHJpbmcuY2FsbChpdCkgPT09ICdbb2JqZWN0IEFycmF5XSc7CiAgICB9CgogICAgLyoqCiAgICAgKiBIZWxwZXIgZnVuY3Rpb24gZm9yIGl0ZXJhdGluZyBvdmVyIGFuIGFycmF5LiBJZiB0aGUgZnVuYyByZXR1cm5zCiAgICAgKiBhIHRydWUgdmFsdWUsIGl0IHdpbGwgYnJlYWsgb3V0IG9mIHRoZSBsb29wLgogICAgICovCiAgICBmdW5jdGlvbiBlYWNoKGFyeSwgZnVuYykgewogICAgICAgIGlmIChhcnkpIHsKICAgICAgICAgICAgdmFyIGk7CiAgICAgICAgICAgIGZvciAoaSA9IDA7IGkgPCBhcnkubGVuZ3RoOyBpICs9IDEpIHsKICAgICAgICAgICAgICAgIGlmIChhcnlbaV0gJiYgZnVuYyhhcnlbaV0sIGksIGFyeSkpIHsKICAgICAgICAgICAgICAgICAgICBicmVhazsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgIH0KICAgIH0KCiAgICAvKioKICAgICAqIEhlbHBlciBmdW5jdGlvbiBmb3IgaXRlcmF0aW5nIG92ZXIgYW4gYXJyYXkgYmFja3dhcmRzLiBJZiB0aGUgZnVuYwogICAgICogcmV0dXJucyBhIHRydWUgdmFsdWUsIGl0IHdpbGwgYnJlYWsgb3V0IG9mIHRoZSBsb29wLgogICAgICovCiAgICBmdW5jdGlvbiBlYWNoUmV2ZXJzZShhcnksIGZ1bmMpIHsKICAgICAgICBpZiAoYXJ5KSB7CiAgICAgICAgICAgIHZhciBpOwogICAgICAgICAgICBmb3IgKGkgPSBhcnkubGVuZ3RoIC0gMTsgaSA+IC0xOyBpIC09IDEpIHsKICAgICAgICAgICAgICAgIGlmIChhcnlbaV0gJiYgZnVuYyhhcnlbaV0sIGksIGFyeSkpIHsKICAgICAgICAgICAgICAgICAgICBicmVhazsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgIH0KICAgIH0KCiAgICBmdW5jdGlvbiBoYXNQcm9wKG9iaiwgcHJvcCkgewogICAgICAgIHJldHVybiBoYXNPd24uY2FsbChvYmosIHByb3ApOwogICAgfQoKICAgIGZ1bmN0aW9uIGdldE93bihvYmosIHByb3ApIHsKICAgICAgICByZXR1cm4gaGFzUHJvcChvYmosIHByb3ApICYmIG9ialtwcm9wXTsKICAgIH0KCiAgICAvKioKICAgICAqIEN5Y2xlcyBvdmVyIHByb3BlcnRpZXMgaW4gYW4gb2JqZWN0IGFuZCBjYWxscyBhIGZ1bmN0aW9uIGZvciBlYWNoCiAgICAgKiBwcm9wZXJ0eSB2YWx1ZS4gSWYgdGhlIGZ1bmN0aW9uIHJldHVybnMgYSB0cnV0aHkgdmFsdWUsIHRoZW4gdGhlCiAgICAgKiBpdGVyYXRpb24gaXMgc3RvcHBlZC4KICAgICAqLwogICAgZnVuY3Rpb24gZWFjaFByb3Aob2JqLCBmdW5jKSB7CiAgICAgICAgdmFyIHByb3A7CiAgICAgICAgZm9yIChwcm9wIGluIG9iaikgewogICAgICAgICAgICBpZiAoaGFzUHJvcChvYmosIHByb3ApKSB7CiAgICAgICAgICAgICAgICBpZiAoZnVuYyhvYmpbcHJvcF0sIHByb3ApKSB7CiAgICAgICAgICAgICAgICAgICAgYnJlYWs7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICB9CgogICAgLyoqCiAgICAgKiBTaW1wbGUgZnVuY3Rpb24gdG8gbWl4IGluIHByb3BlcnRpZXMgZnJvbSBzb3VyY2UgaW50byB0YXJnZXQsCiAgICAgKiBidXQgb25seSBpZiB0YXJnZXQgZG9lcyBub3QgYWxyZWFkeSBoYXZlIGEgcHJvcGVydHkgb2YgdGhlIHNhbWUgbmFtZS4KICAgICAqLwogICAgZnVuY3Rpb24gbWl4aW4odGFyZ2V0LCBzb3VyY2UsIGZvcmNlLCBkZWVwU3RyaW5nTWl4aW4pIHsKICAgICAgICBpZiAoc291cmNlKSB7CiAgICAgICAgICAgIGVhY2hQcm9wKHNvdXJjZSwgZnVuY3Rpb24gKHZhbHVlLCBwcm9wKSB7CiAgICAgICAgICAgICAgICBpZiAoZm9yY2UgfHwgIWhhc1Byb3AodGFyZ2V0LCBwcm9wKSkgewogICAgICAgICAgICAgICAgICAgIGlmIChkZWVwU3RyaW5nTWl4aW4gJiYgdHlwZW9mIHZhbHVlID09PSAnb2JqZWN0JyAmJiB2YWx1ZSAmJgogICAgICAgICAgICAgICAgICAgICAgICAhaXNBcnJheSh2YWx1ZSkgJiYgIWlzRnVuY3Rpb24odmFsdWUpICYmCiAgICAgICAgICAgICAgICAgICAgICAgICEodmFsdWUgaW5zdGFuY2VvZiBSZWdFeHApKSB7CgogICAgICAgICAgICAgICAgICAgICAgICBpZiAoIXRhcmdldFtwcm9wXSkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgdGFyZ2V0W3Byb3BdID0ge307CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgbWl4aW4odGFyZ2V0W3Byb3BdLCB2YWx1ZSwgZm9yY2UsIGRlZXBTdHJpbmdNaXhpbik7CiAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgICAgICAgICAgICAgdGFyZ2V0W3Byb3BdID0gdmFsdWU7CiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9KTsKICAgICAgICB9CiAgICAgICAgcmV0dXJuIHRhcmdldDsKICAgIH0KCiAgICAvL1NpbWlsYXIgdG8gRnVuY3Rpb24ucHJvdG90eXBlLmJpbmQsIGJ1dCB0aGUgJ3RoaXMnIG9iamVjdCBpcyBzcGVjaWZpZWQKICAgIC8vZmlyc3QsIHNpbmNlIGl0IGlzIGVhc2llciB0byByZWFkL2ZpZ3VyZSBvdXQgd2hhdCAndGhpcycgd2lsbCBiZS4KICAgIGZ1bmN0aW9uIGJpbmQob2JqLCBmbikgewogICAgICAgIHJldHVybiBmdW5jdGlvbiAoKSB7CiAgICAgICAgICAgIHJldHVybiBmbi5hcHBseShvYmosIGFyZ3VtZW50cyk7CiAgICAgICAgfTsKICAgIH0KCiAgICBmdW5jdGlvbiBzY3JpcHRzKCkgewogICAgICAgIHJldHVybiBkb2N1bWVudC5nZXRFbGVtZW50c0J5VGFnTmFtZSgnc2NyaXB0Jyk7CiAgICB9CgogICAgZnVuY3Rpb24gZGVmYXVsdE9uRXJyb3IoZXJyKSB7CiAgICAgICAgdGhyb3cgZXJyOwogICAgfQoKICAgIC8vQWxsb3cgZ2V0dGluZyBhIGdsb2JhbCB0aGF0IGlzIGV4cHJlc3NlZCBpbgogICAgLy9kb3Qgbm90YXRpb24sIGxpa2UgJ2EuYi5jJy4KICAgIGZ1bmN0aW9uIGdldEdsb2JhbCh2YWx1ZSkgewogICAgICAgIGlmICghdmFsdWUpIHsKICAgICAgICAgICAgcmV0dXJuIHZhbHVlOwogICAgICAgIH0KICAgICAgICB2YXIgZyA9IGdsb2JhbDsKICAgICAgICBlYWNoKHZhbHVlLnNwbGl0KCcuJyksIGZ1bmN0aW9uIChwYXJ0KSB7CiAgICAgICAgICAgIGcgPSBnW3BhcnRdOwogICAgICAgIH0pOwogICAgICAgIHJldHVybiBnOwogICAgfQoKICAgIC8qKgogICAgICogQ29uc3RydWN0cyBhbiBlcnJvciB3aXRoIGEgcG9pbnRlciB0byBhbiBVUkwgd2l0aCBtb3JlIGluZm9ybWF0aW9uLgogICAgICogQHBhcmFtIHtTdHJpbmd9IGlkIHRoZSBlcnJvciBJRCB0aGF0IG1hcHMgdG8gYW4gSUQgb24gYSB3ZWIgcGFnZS4KICAgICAqIEBwYXJhbSB7U3RyaW5nfSBtZXNzYWdlIGh1bWFuIHJlYWRhYmxlIGVycm9yLgogICAgICogQHBhcmFtIHtFcnJvcn0gW2Vycl0gdGhlIG9yaWdpbmFsIGVycm9yLCBpZiB0aGVyZSBpcyBvbmUuCiAgICAgKgogICAgICogQHJldHVybnMge0Vycm9yfQogICAgICovCiAgICBmdW5jdGlvbiBtYWtlRXJyb3IoaWQsIG1zZywgZXJyLCByZXF1aXJlTW9kdWxlcykgewogICAgICAgIHZhciBlID0gbmV3IEVycm9yKG1zZyArICdcbmh0dHA6Ly9yZXF1aXJlanMub3JnL2RvY3MvZXJyb3JzLmh0bWwjJyArIGlkKTsKICAgICAgICBlLnJlcXVpcmVUeXBlID0gaWQ7CiAgICAgICAgZS5yZXF1aXJlTW9kdWxlcyA9IHJlcXVpcmVNb2R1bGVzOwogICAgICAgIGlmIChlcnIpIHsKICAgICAgICAgICAgZS5vcmlnaW5hbEVycm9yID0gZXJyOwogICAgICAgIH0KICAgICAgICByZXR1cm4gZTsKICAgIH0KCiAgICBpZiAodHlwZW9mIGRlZmluZSAhPT0gJ3VuZGVmaW5lZCcpIHsKICAgICAgICAvL0lmIGEgZGVmaW5lIGlzIGFscmVhZHkgaW4gcGxheSB2aWEgYW5vdGhlciBBTUQgbG9hZGVyLAogICAgICAgIC8vZG8gbm90IG92ZXJ3cml0ZS4KICAgICAgICByZXR1cm47CiAgICB9CgogICAgaWYgKHR5cGVvZiByZXF1aXJlanMgIT09ICd1bmRlZmluZWQnKSB7CiAgICAgICAgaWYgKGlzRnVuY3Rpb24ocmVxdWlyZWpzKSkgewogICAgICAgICAgICAvL0RvIG5vdCBvdmVyd3JpdGUgYW4gZXhpc3RpbmcgcmVxdWlyZWpzIGluc3RhbmNlLgogICAgICAgICAgICByZXR1cm47CiAgICAgICAgfQogICAgICAgIGNmZyA9IHJlcXVpcmVqczsKICAgICAgICByZXF1aXJlanMgPSB1bmRlZmluZWQ7CiAgICB9CgogICAgLy9BbGxvdyBmb3IgYSByZXF1aXJlIGNvbmZpZyBvYmplY3QKICAgIGlmICh0eXBlb2YgcmVxdWlyZSAhPT0gJ3VuZGVmaW5lZCcgJiYgIWlzRnVuY3Rpb24ocmVxdWlyZSkpIHsKICAgICAgICAvL2Fzc3VtZSBpdCBpcyBhIGNvbmZpZyBvYmplY3QuCiAgICAgICAgY2ZnID0gcmVxdWlyZTsKICAgICAgICByZXF1aXJlID0gdW5kZWZpbmVkOwogICAgfQoKICAgIGZ1bmN0aW9uIG5ld0NvbnRleHQoY29udGV4dE5hbWUpIHsKICAgICAgICB2YXIgaW5DaGVja0xvYWRlZCwgTW9kdWxlLCBjb250ZXh0LCBoYW5kbGVycywKICAgICAgICAgICAgY2hlY2tMb2FkZWRUaW1lb3V0SWQsCiAgICAgICAgICAgIGNvbmZpZyA9IHsKICAgICAgICAgICAgICAgIC8vRGVmYXVsdHMuIERvIG5vdCBzZXQgYSBkZWZhdWx0IGZvciBtYXAKICAgICAgICAgICAgICAgIC8vY29uZmlnIHRvIHNwZWVkIHVwIG5vcm1hbGl6ZSgpLCB3aGljaAogICAgICAgICAgICAgICAgLy93aWxsIHJ1biBmYXN0ZXIgaWYgdGhlcmUgaXMgbm8gZGVmYXVsdC4KICAgICAgICAgICAgICAgIHdhaXRTZWNvbmRzOiA3LAogICAgICAgICAgICAgICAgYmFzZVVybDogJy4vJywKICAgICAgICAgICAgICAgIHBhdGhzOiB7fSwKICAgICAgICAgICAgICAgIGJ1bmRsZXM6IHt9LAogICAgICAgICAgICAgICAgcGtnczoge30sCiAgICAgICAgICAgICAgICBzaGltOiB7fSwKICAgICAgICAgICAgICAgIGNvbmZpZzoge30KICAgICAgICAgICAgfSwKICAgICAgICAgICAgcmVnaXN0cnkgPSB7fSwKICAgICAgICAgICAgLy9yZWdpc3RyeSBvZiBqdXN0IGVuYWJsZWQgbW9kdWxlcywgdG8gc3BlZWQKICAgICAgICAgICAgLy9jeWNsZSBicmVha2luZyBjb2RlIHdoZW4gbG90cyBvZiBtb2R1bGVzCiAgICAgICAgICAgIC8vYXJlIHJlZ2lzdGVyZWQsIGJ1dCBub3QgYWN0aXZhdGVkLgogICAgICAgICAgICBlbmFibGVkUmVnaXN0cnkgPSB7fSwKICAgICAgICAgICAgdW5kZWZFdmVudHMgPSB7fSwKICAgICAgICAgICAgZGVmUXVldWUgPSBbXSwKICAgICAgICAgICAgZGVmaW5lZCA9IHt9LAogICAgICAgICAgICB1cmxGZXRjaGVkID0ge30sCiAgICAgICAgICAgIGJ1bmRsZXNNYXAgPSB7fSwKICAgICAgICAgICAgcmVxdWlyZUNvdW50ZXIgPSAxLAogICAgICAgICAgICB1bm5vcm1hbGl6ZWRDb3VudGVyID0gMTsKCiAgICAgICAgLyoqCiAgICAgICAgICogVHJpbXMgdGhlIC4gYW5kIC4uIGZyb20gYW4gYXJyYXkgb2YgcGF0aCBzZWdtZW50cy4KICAgICAgICAgKiBJdCB3aWxsIGtlZXAgYSBsZWFkaW5nIHBhdGggc2VnbWVudCBpZiBhIC4uIHdpbGwgYmVjb21lCiAgICAgICAgICogdGhlIGZpcnN0IHBhdGggc2VnbWVudCwgdG8gaGVscCB3aXRoIG1vZHVsZSBuYW1lIGxvb2t1cHMsCiAgICAgICAgICogd2hpY2ggYWN0IGxpa2UgcGF0aHMsIGJ1dCBjYW4gYmUgcmVtYXBwZWQuIEJ1dCB0aGUgZW5kIHJlc3VsdCwKICAgICAgICAgKiBhbGwgcGF0aHMgdGhhdCB1c2UgdGhpcyBmdW5jdGlvbiBzaG91bGQgbG9vayBub3JtYWxpemVkLgogICAgICAgICAqIE5PVEU6IHRoaXMgbWV0aG9kIE1PRElGSUVTIHRoZSBpbnB1dCBhcnJheS4KICAgICAgICAgKiBAcGFyYW0ge0FycmF5fSBhcnkgdGhlIGFycmF5IG9mIHBhdGggc2VnbWVudHMuCiAgICAgICAgICovCiAgICAgICAgZnVuY3Rpb24gdHJpbURvdHMoYXJ5KSB7CiAgICAgICAgICAgIHZhciBpLCBwYXJ0OwogICAgICAgICAgICBmb3IgKGkgPSAwOyBpIDwgYXJ5Lmxlbmd0aDsgaSsrKSB7CiAgICAgICAgICAgICAgICBwYXJ0ID0gYXJ5W2ldOwogICAgICAgICAgICAgICAgaWYgKHBhcnQgPT09ICcuJykgewogICAgICAgICAgICAgICAgICAgIGFyeS5zcGxpY2UoaSwgMSk7CiAgICAgICAgICAgICAgICAgICAgaSAtPSAxOwogICAgICAgICAgICAgICAgfSBlbHNlIGlmIChwYXJ0ID09PSAnLi4nKSB7CiAgICAgICAgICAgICAgICAgICAgLy8gSWYgYXQgdGhlIHN0YXJ0LCBvciBwcmV2aW91cyB2YWx1ZSBpcyBzdGlsbCAuLiwKICAgICAgICAgICAgICAgICAgICAvLyBrZWVwIHRoZW0gc28gdGhhdCB3aGVuIGNvbnZlcnRlZCB0byBhIHBhdGggaXQgbWF5CiAgICAgICAgICAgICAgICAgICAgLy8gc3RpbGwgd29yayB3aGVuIGNvbnZlcnRlZCB0byBhIHBhdGgsIGV2ZW4gdGhvdWdoCiAgICAgICAgICAgICAgICAgICAgLy8gYXMgYW4gSUQgaXQgaXMgbGVzcyB0aGFuIGlkZWFsLiBJbiBsYXJnZXIgcG9pbnQKICAgICAgICAgICAgICAgICAgICAvLyByZWxlYXNlcywgbWF5IGJlIGJldHRlciB0byBqdXN0IGtpY2sgb3V0IGFuIGVycm9yLgogICAgICAgICAgICAgICAgICAgIGlmIChpID09PSAwIHx8IChpID09PSAxICYmIGFyeVsyXSA9PT0gJy4uJykgfHwgYXJ5W2kgLSAxXSA9PT0gJy4uJykgewogICAgICAgICAgICAgICAgICAgICAgICBjb250aW51ZTsKICAgICAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKGkgPiAwKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIGFyeS5zcGxpY2UoaSAtIDEsIDIpOwogICAgICAgICAgICAgICAgICAgICAgICBpIC09IDI7CiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CiAgICAgICAgfQoKICAgICAgICAvKioKICAgICAgICAgKiBHaXZlbiBhIHJlbGF0aXZlIG1vZHVsZSBuYW1lLCBsaWtlIC4vc29tZXRoaW5nLCBub3JtYWxpemUgaXQgdG8KICAgICAgICAgKiBhIHJlYWwgbmFtZSB0aGF0IGNhbiBiZSBtYXBwZWQgdG8gYSBwYXRoLgogICAgICAgICAqIEBwYXJhbSB7U3RyaW5nfSBuYW1lIHRoZSByZWxhdGl2ZSBuYW1lCiAgICAgICAgICogQHBhcmFtIHtTdHJpbmd9IGJhc2VOYW1lIGEgcmVhbCBuYW1lIHRoYXQgdGhlIG5hbWUgYXJnIGlzIHJlbGF0aXZlCiAgICAgICAgICogdG8uCiAgICAgICAgICogQHBhcmFtIHtCb29sZWFufSBhcHBseU1hcCBhcHBseSB0aGUgbWFwIGNvbmZpZyB0byB0aGUgdmFsdWUuIFNob3VsZAogICAgICAgICAqIG9ubHkgYmUgZG9uZSBpZiB0aGlzIG5vcm1hbGl6YXRpb24gaXMgZm9yIGEgZGVwZW5kZW5jeSBJRC4KICAgICAgICAgKiBAcmV0dXJucyB7U3RyaW5nfSBub3JtYWxpemVkIG5hbWUKICAgICAgICAgKi8KICAgICAgICBmdW5jdGlvbiBub3JtYWxpemUobmFtZSwgYmFzZU5hbWUsIGFwcGx5TWFwKSB7CiAgICAgICAgICAgIHZhciBwa2dNYWluLCBtYXBWYWx1ZSwgbmFtZVBhcnRzLCBpLCBqLCBuYW1lU2VnbWVudCwgbGFzdEluZGV4LAogICAgICAgICAgICAgICAgZm91bmRNYXAsIGZvdW5kSSwgZm91bmRTdGFyTWFwLCBzdGFySSwgbm9ybWFsaXplZEJhc2VQYXJ0cywKICAgICAgICAgICAgICAgIGJhc2VQYXJ0cyA9IChiYXNlTmFtZSAmJiBiYXNlTmFtZS5zcGxpdCgnLycpKSwKICAgICAgICAgICAgICAgIG1hcCA9IGNvbmZpZy5tYXAsCiAgICAgICAgICAgICAgICBzdGFyTWFwID0gbWFwICYmIG1hcFsnKiddOwoKICAgICAgICAgICAgLy9BZGp1c3QgYW55IHJlbGF0aXZlIHBhdGhzLgogICAgICAgICAgICBpZiAobmFtZSkgewogICAgICAgICAgICAgICAgbmFtZSA9IG5hbWUuc3BsaXQoJy8nKTsKICAgICAgICAgICAgICAgIGxhc3RJbmRleCA9IG5hbWUubGVuZ3RoIC0gMTsKCiAgICAgICAgICAgICAgICAvLyBJZiB3YW50aW5nIG5vZGUgSUQgY29tcGF0aWJpbGl0eSwgc3RyaXAgLmpzIGZyb20gZW5kCiAgICAgICAgICAgICAgICAvLyBvZiBJRHMuIEhhdmUgdG8gZG8gdGhpcyBoZXJlLCBhbmQgbm90IGluIG5hbWVUb1VybAogICAgICAgICAgICAgICAgLy8gYmVjYXVzZSBub2RlIGFsbG93cyBlaXRoZXIgLmpzIG9yIG5vbiAuanMgdG8gbWFwCiAgICAgICAgICAgICAgICAvLyB0byBzYW1lIGZpbGUuCiAgICAgICAgICAgICAgICBpZiAoY29uZmlnLm5vZGVJZENvbXBhdCAmJiBqc1N1ZmZpeFJlZ0V4cC50ZXN0KG5hbWVbbGFzdEluZGV4XSkpIHsKICAgICAgICAgICAgICAgICAgICBuYW1lW2xhc3RJbmRleF0gPSBuYW1lW2xhc3RJbmRleF0ucmVwbGFjZShqc1N1ZmZpeFJlZ0V4cCwgJycpOwogICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgIC8vIFN0YXJ0cyB3aXRoIGEgJy4nIHNvIG5lZWQgdGhlIGJhc2VOYW1lCiAgICAgICAgICAgICAgICBpZiAobmFtZVswXS5jaGFyQXQoMCkgPT09ICcuJyAmJiBiYXNlUGFydHMpIHsKICAgICAgICAgICAgICAgICAgICAvL0NvbnZlcnQgYmFzZU5hbWUgdG8gYXJyYXksIGFuZCBsb3Agb2ZmIHRoZSBsYXN0IHBhcnQsCiAgICAgICAgICAgICAgICAgICAgLy9zbyB0aGF0IC4gbWF0Y2hlcyB0aGF0ICdkaXJlY3RvcnknIGFuZCBub3QgbmFtZSBvZiB0aGUgYmFzZU5hbWUncwogICAgICAgICAgICAgICAgICAgIC8vbW9kdWxlLiBGb3IgaW5zdGFuY2UsIGJhc2VOYW1lIG9mICdvbmUvdHdvL3RocmVlJywgbWFwcyB0bwogICAgICAgICAgICAgICAgICAgIC8vJ29uZS90d28vdGhyZWUuanMnLCBidXQgd2Ugd2FudCB0aGUgZGlyZWN0b3J5LCAnb25lL3R3bycgZm9yCiAgICAgICAgICAgICAgICAgICAgLy90aGlzIG5vcm1hbGl6YXRpb24uCiAgICAgICAgICAgICAgICAgICAgbm9ybWFsaXplZEJhc2VQYXJ0cyA9IGJhc2VQYXJ0cy5zbGljZSgwLCBiYXNlUGFydHMubGVuZ3RoIC0gMSk7CiAgICAgICAgICAgICAgICAgICAgbmFtZSA9IG5vcm1hbGl6ZWRCYXNlUGFydHMuY29uY2F0KG5hbWUpOwogICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgIHRyaW1Eb3RzKG5hbWUpOwogICAgICAgICAgICAgICAgbmFtZSA9IG5hbWUuam9pbignLycpOwogICAgICAgICAgICB9CgogICAgICAgICAgICAvL0FwcGx5IG1hcCBjb25maWcgaWYgYXZhaWxhYmxlLgogICAgICAgICAgICBpZiAoYXBwbHlNYXAgJiYgbWFwICYmIChiYXNlUGFydHMgfHwgc3Rhck1hcCkpIHsKICAgICAgICAgICAgICAgIG5hbWVQYXJ0cyA9IG5hbWUuc3BsaXQoJy8nKTsKCiAgICAgICAgICAgICAgICBvdXRlckxvb3A6IGZvciAoaSA9IG5hbWVQYXJ0cy5sZW5ndGg7IGkgPiAwOyBpIC09IDEpIHsKICAgICAgICAgICAgICAgICAgICBuYW1lU2VnbWVudCA9IG5hbWVQYXJ0cy5zbGljZSgwLCBpKS5qb2luKCcvJyk7CgogICAgICAgICAgICAgICAgICAgIGlmIChiYXNlUGFydHMpIHsKICAgICAgICAgICAgICAgICAgICAgICAgLy9GaW5kIHRoZSBsb25nZXN0IGJhc2VOYW1lIHNlZ21lbnQgbWF0Y2ggaW4gdGhlIGNvbmZpZy4KICAgICAgICAgICAgICAgICAgICAgICAgLy9TbywgZG8gam9pbnMgb24gdGhlIGJpZ2dlc3QgdG8gc21hbGxlc3QgbGVuZ3RocyBvZiBiYXNlUGFydHMuCiAgICAgICAgICAgICAgICAgICAgICAgIGZvciAoaiA9IGJhc2VQYXJ0cy5sZW5ndGg7IGogPiAwOyBqIC09IDEpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1hcFZhbHVlID0gZ2V0T3duKG1hcCwgYmFzZVBhcnRzLnNsaWNlKDAsIGopLmpvaW4oJy8nKSk7CgogICAgICAgICAgICAgICAgICAgICAgICAgICAgLy9iYXNlTmFtZSBzZWdtZW50IGhhcyBjb25maWcsIGZpbmQgaWYgaXQgaGFzIG9uZSBmb3IKICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vdGhpcyBuYW1lLgogICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKG1hcFZhbHVlKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWFwVmFsdWUgPSBnZXRPd24obWFwVmFsdWUsIG5hbWVTZWdtZW50KTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAobWFwVmFsdWUpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy9NYXRjaCwgdXBkYXRlIG5hbWUgdG8gdGhlIG5ldyB2YWx1ZS4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZm91bmRNYXAgPSBtYXBWYWx1ZTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZm91bmRJID0gaTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWsgb3V0ZXJMb29wOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICAgICAgLy9DaGVjayBmb3IgYSBzdGFyIG1hcCBtYXRjaCwgYnV0IGp1c3QgaG9sZCBvbiB0byBpdCwKICAgICAgICAgICAgICAgICAgICAvL2lmIHRoZXJlIGlzIGEgc2hvcnRlciBzZWdtZW50IG1hdGNoIGxhdGVyIGluIGEgbWF0Y2hpbmcKICAgICAgICAgICAgICAgICAgICAvL2NvbmZpZywgdGhlbiBmYXZvciBvdmVyIHRoaXMgc3RhciBtYXAuCiAgICAgICAgICAgICAgICAgICAgaWYgKCFmb3VuZFN0YXJNYXAgJiYgc3Rhck1hcCAmJiBnZXRPd24oc3Rhck1hcCwgbmFtZVNlZ21lbnQpKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIGZvdW5kU3Rhck1hcCA9IGdldE93bihzdGFyTWFwLCBuYW1lU2VnbWVudCk7CiAgICAgICAgICAgICAgICAgICAgICAgIHN0YXJJID0gaTsKICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgaWYgKCFmb3VuZE1hcCAmJiBmb3VuZFN0YXJNYXApIHsKICAgICAgICAgICAgICAgICAgICBmb3VuZE1hcCA9IGZvdW5kU3Rhck1hcDsKICAgICAgICAgICAgICAgICAgICBmb3VuZEkgPSBzdGFySTsKICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICBpZiAoZm91bmRNYXApIHsKICAgICAgICAgICAgICAgICAgICBuYW1lUGFydHMuc3BsaWNlKDAsIGZvdW5kSSwgZm91bmRNYXApOwogICAgICAgICAgICAgICAgICAgIG5hbWUgPSBuYW1lUGFydHMuam9pbignLycpOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CgogICAgICAgICAgICAvLyBJZiB0aGUgbmFtZSBwb2ludHMgdG8gYSBwYWNrYWdlJ3MgbmFtZSwgdXNlCiAgICAgICAgICAgIC8vIHRoZSBwYWNrYWdlIG1haW4gaW5zdGVhZC4KICAgICAgICAgICAgcGtnTWFpbiA9IGdldE93bihjb25maWcucGtncywgbmFtZSk7CgogICAgICAgICAgICByZXR1cm4gcGtnTWFpbiA/IHBrZ01haW4gOiBuYW1lOwogICAgICAgIH0KCiAgICAgICAgZnVuY3Rpb24gcmVtb3ZlU2NyaXB0KG5hbWUpIHsKICAgICAgICAgICAgaWYgKGlzQnJvd3NlcikgewogICAgICAgICAgICAgICAgZWFjaChzY3JpcHRzKCksIGZ1bmN0aW9uIChzY3JpcHROb2RlKSB7CiAgICAgICAgICAgICAgICAgICAgaWYgKHNjcmlwdE5vZGUuZ2V0QXR0cmlidXRlKCdkYXRhLXJlcXVpcmVtb2R1bGUnKSA9PT0gbmFtZSAmJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgc2NyaXB0Tm9kZS5nZXRBdHRyaWJ1dGUoJ2RhdGEtcmVxdWlyZWNvbnRleHQnKSA9PT0gY29udGV4dC5jb250ZXh0TmFtZSkgewogICAgICAgICAgICAgICAgICAgICAgICBzY3JpcHROb2RlLnBhcmVudE5vZGUucmVtb3ZlQ2hpbGQoc2NyaXB0Tm9kZSk7CiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiB0cnVlOwogICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIH0pOwogICAgICAgICAgICB9CiAgICAgICAgfQoKICAgICAgICBmdW5jdGlvbiBoYXNQYXRoRmFsbGJhY2soaWQpIHsKICAgICAgICAgICAgdmFyIHBhdGhDb25maWcgPSBnZXRPd24oY29uZmlnLnBhdGhzLCBpZCk7CiAgICAgICAgICAgIGlmIChwYXRoQ29uZmlnICYmIGlzQXJyYXkocGF0aENvbmZpZykgJiYgcGF0aENvbmZpZy5sZW5ndGggPiAxKSB7CiAgICAgICAgICAgICAgICAvL1BvcCBvZmYgdGhlIGZpcnN0IGFycmF5IHZhbHVlLCBzaW5jZSBpdCBmYWlsZWQsIGFuZAogICAgICAgICAgICAgICAgLy9yZXRyeQogICAgICAgICAgICAgICAgcGF0aENvbmZpZy5zaGlmdCgpOwogICAgICAgICAgICAgICAgY29udGV4dC5yZXF1aXJlLnVuZGVmKGlkKTsKCiAgICAgICAgICAgICAgICAvL0N1c3RvbSByZXF1aXJlIHRoYXQgZG9lcyBub3QgZG8gbWFwIHRyYW5zbGF0aW9uLCBzaW5jZQogICAgICAgICAgICAgICAgLy9JRCBpcyAiYWJzb2x1dGUiLCBhbHJlYWR5IG1hcHBlZC9yZXNvbHZlZC4KICAgICAgICAgICAgICAgIGNvbnRleHQubWFrZVJlcXVpcmUobnVsbCwgewogICAgICAgICAgICAgICAgICAgIHNraXBNYXA6IHRydWUKICAgICAgICAgICAgICAgIH0pKFtpZF0pOwoKICAgICAgICAgICAgICAgIHJldHVybiB0cnVlOwogICAgICAgICAgICB9CiAgICAgICAgfQoKICAgICAgICAvL1R1cm5zIGEgcGx1Z2luIXJlc291cmNlIHRvIFtwbHVnaW4sIHJlc291cmNlXQogICAgICAgIC8vd2l0aCB0aGUgcGx1Z2luIGJlaW5nIHVuZGVmaW5lZCBpZiB0aGUgbmFtZQogICAgICAgIC8vZGlkIG5vdCBoYXZlIGEgcGx1Z2luIHByZWZpeC4KICAgICAgICBmdW5jdGlvbiBzcGxpdFByZWZpeChuYW1lKSB7CiAgICAgICAgICAgIHZhciBwcmVmaXgsCiAgICAgICAgICAgICAgICBpbmRleCA9IG5hbWUgPyBuYW1lLmluZGV4T2YoJyEnKSA6IC0xOwogICAgICAgICAgICBpZiAoaW5kZXggPiAtMSkgewogICAgICAgICAgICAgICAgcHJlZml4ID0gbmFtZS5zdWJzdHJpbmcoMCwgaW5kZXgpOwogICAgICAgICAgICAgICAgbmFtZSA9IG5hbWUuc3Vic3RyaW5nKGluZGV4ICsgMSwgbmFtZS5sZW5ndGgpOwogICAgICAgICAgICB9CiAgICAgICAgICAgIHJldHVybiBbcHJlZml4LCBuYW1lXTsKICAgICAgICB9CgogICAgICAgIC8qKgogICAgICAgICAqIENyZWF0ZXMgYSBtb2R1bGUgbWFwcGluZyB0aGF0IGluY2x1ZGVzIHBsdWdpbiBwcmVmaXgsIG1vZHVsZQogICAgICAgICAqIG5hbWUsIGFuZCBwYXRoLiBJZiBwYXJlbnRNb2R1bGVNYXAgaXMgcHJvdmlkZWQgaXQgd2lsbAogICAgICAgICAqIGFsc28gbm9ybWFsaXplIHRoZSBuYW1lIHZpYSByZXF1aXJlLm5vcm1hbGl6ZSgpCiAgICAgICAgICoKICAgICAgICAgKiBAcGFyYW0ge1N0cmluZ30gbmFtZSB0aGUgbW9kdWxlIG5hbWUKICAgICAgICAgKiBAcGFyYW0ge1N0cmluZ30gW3BhcmVudE1vZHVsZU1hcF0gcGFyZW50IG1vZHVsZSBtYXAKICAgICAgICAgKiBmb3IgdGhlIG1vZHVsZSBuYW1lLCB1c2VkIHRvIHJlc29sdmUgcmVsYXRpdmUgbmFtZXMuCiAgICAgICAgICogQHBhcmFtIHtCb29sZWFufSBpc05vcm1hbGl6ZWQ6IGlzIHRoZSBJRCBhbHJlYWR5IG5vcm1hbGl6ZWQuCiAgICAgICAgICogVGhpcyBpcyB0cnVlIGlmIHRoaXMgY2FsbCBpcyBkb25lIGZvciBhIGRlZmluZSgpIG1vZHVsZSBJRC4KICAgICAgICAgKiBAcGFyYW0ge0Jvb2xlYW59IGFwcGx5TWFwOiBhcHBseSB0aGUgbWFwIGNvbmZpZyB0byB0aGUgSUQuCiAgICAgICAgICogU2hvdWxkIG9ubHkgYmUgdHJ1ZSBpZiB0aGlzIG1hcCBpcyBmb3IgYSBkZXBlbmRlbmN5LgogICAgICAgICAqCiAgICAgICAgICogQHJldHVybnMge09iamVjdH0KICAgICAgICAgKi8KICAgICAgICBmdW5jdGlvbiBtYWtlTW9kdWxlTWFwKG5hbWUsIHBhcmVudE1vZHVsZU1hcCwgaXNOb3JtYWxpemVkLCBhcHBseU1hcCkgewogICAgICAgICAgICB2YXIgdXJsLCBwbHVnaW5Nb2R1bGUsIHN1ZmZpeCwgbmFtZVBhcnRzLAogICAgICAgICAgICAgICAgcHJlZml4ID0gbnVsbCwKICAgICAgICAgICAgICAgIHBhcmVudE5hbWUgPSBwYXJlbnRNb2R1bGVNYXAgPyBwYXJlbnRNb2R1bGVNYXAubmFtZSA6IG51bGwsCiAgICAgICAgICAgICAgICBvcmlnaW5hbE5hbWUgPSBuYW1lLAogICAgICAgICAgICAgICAgaXNEZWZpbmUgPSB0cnVlLAogICAgICAgICAgICAgICAgbm9ybWFsaXplZE5hbWUgPSAnJzsKCiAgICAgICAgICAgIC8vSWYgbm8gbmFtZSwgdGhlbiBpdCBtZWFucyBpdCBpcyBhIHJlcXVpcmUgY2FsbCwgZ2VuZXJhdGUgYW4KICAgICAgICAgICAgLy9pbnRlcm5hbCBuYW1lLgogICAgICAgICAgICBpZiAoIW5hbWUpIHsKICAgICAgICAgICAgICAgIGlzRGVmaW5lID0gZmFsc2U7CiAgICAgICAgICAgICAgICBuYW1lID0gJ19AcicgKyAocmVxdWlyZUNvdW50ZXIgKz0gMSk7CiAgICAgICAgICAgIH0KCiAgICAgICAgICAgIG5hbWVQYXJ0cyA9IHNwbGl0UHJlZml4KG5hbWUpOwogICAgICAgICAgICBwcmVmaXggPSBuYW1lUGFydHNbMF07CiAgICAgICAgICAgIG5hbWUgPSBuYW1lUGFydHNbMV07CgogICAgICAgICAgICBpZiAocHJlZml4KSB7CiAgICAgICAgICAgICAgICBwcmVmaXggPSBub3JtYWxpemUocHJlZml4LCBwYXJlbnROYW1lLCBhcHBseU1hcCk7CiAgICAgICAgICAgICAgICBwbHVnaW5Nb2R1bGUgPSBnZXRPd24oZGVmaW5lZCwgcHJlZml4KTsKICAgICAgICAgICAgfQoKICAgICAgICAgICAgLy9BY2NvdW50IGZvciByZWxhdGl2ZSBwYXRocyBpZiB0aGVyZSBpcyBhIGJhc2UgbmFtZS4KICAgICAgICAgICAgaWYgKG5hbWUpIHsKICAgICAgICAgICAgICAgIGlmIChwcmVmaXgpIHsKICAgICAgICAgICAgICAgICAgICBpZiAocGx1Z2luTW9kdWxlICYmIHBsdWdpbk1vZHVsZS5ub3JtYWxpemUpIHsKICAgICAgICAgICAgICAgICAgICAgICAgLy9QbHVnaW4gaXMgbG9hZGVkLCB1c2UgaXRzIG5vcm1hbGl6ZSBtZXRob2QuCiAgICAgICAgICAgICAgICAgICAgICAgIG5vcm1hbGl6ZWROYW1lID0gcGx1Z2luTW9kdWxlLm5vcm1hbGl6ZShuYW1lLCBmdW5jdGlvbiAobmFtZSkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIG5vcm1hbGl6ZShuYW1lLCBwYXJlbnROYW1lLCBhcHBseU1hcCk7CiAgICAgICAgICAgICAgICAgICAgICAgIH0pOwogICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgICAgICAgICAgICAgIC8vIElmIG5lc3RlZCBwbHVnaW4gcmVmZXJlbmNlcywgdGhlbiBkbyBub3QgdHJ5IHRvCiAgICAgICAgICAgICAgICAgICAgICAgIC8vIG5vcm1hbGl6ZSwgYXMgaXQgd2lsbCBub3Qgbm9ybWFsaXplIGNvcnJlY3RseS4gVGhpcwogICAgICAgICAgICAgICAgICAgICAgICAvLyBwbGFjZXMgYSByZXN0cmljdGlvbiBvbiByZXNvdXJjZUlkcywgYW5kIHRoZSBsb25nZXIKICAgICAgICAgICAgICAgICAgICAgICAgLy8gdGVybSBzb2x1dGlvbiBpcyBub3QgdG8gbm9ybWFsaXplIHVudGlsIHBsdWdpbnMgYXJlCiAgICAgICAgICAgICAgICAgICAgICAgIC8vIGxvYWRlZCBhbmQgYWxsIG5vcm1hbGl6YXRpb25zIHRvIGFsbG93IGZvciBhc3luYwogICAgICAgICAgICAgICAgICAgICAgICAvLyBsb2FkaW5nIG9mIGEgbG9hZGVyIHBsdWdpbi4gQnV0IGZvciBub3csIGZpeGVzIHRoZQogICAgICAgICAgICAgICAgICAgICAgICAvLyBjb21tb24gdXNlcy4gRGV0YWlscyBpbiAjMTEzMQogICAgICAgICAgICAgICAgICAgICAgICBub3JtYWxpemVkTmFtZSA9IG5hbWUuaW5kZXhPZignIScpID09PSAtMSA/CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm9ybWFsaXplKG5hbWUsIHBhcmVudE5hbWUsIGFwcGx5TWFwKSA6CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmFtZTsKICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICAgICAgICAgIC8vQSByZWd1bGFyIG1vZHVsZS4KICAgICAgICAgICAgICAgICAgICBub3JtYWxpemVkTmFtZSA9IG5vcm1hbGl6ZShuYW1lLCBwYXJlbnROYW1lLCBhcHBseU1hcCk7CgogICAgICAgICAgICAgICAgICAgIC8vTm9ybWFsaXplZCBuYW1lIG1heSBiZSBhIHBsdWdpbiBJRCBkdWUgdG8gbWFwIGNvbmZpZwogICAgICAgICAgICAgICAgICAgIC8vYXBwbGljYXRpb24gaW4gbm9ybWFsaXplLiBUaGUgbWFwIGNvbmZpZyB2YWx1ZXMgbXVzdAogICAgICAgICAgICAgICAgICAgIC8vYWxyZWFkeSBiZSBub3JtYWxpemVkLCBzbyBkbyBub3QgbmVlZCB0byByZWRvIHRoYXQgcGFydC4KICAgICAgICAgICAgICAgICAgICBuYW1lUGFydHMgPSBzcGxpdFByZWZpeChub3JtYWxpemVkTmFtZSk7CiAgICAgICAgICAgICAgICAgICAgcHJlZml4ID0gbmFtZVBhcnRzWzBdOwogICAgICAgICAgICAgICAgICAgIG5vcm1hbGl6ZWROYW1lID0gbmFtZVBhcnRzWzFdOwogICAgICAgICAgICAgICAgICAgIGlzTm9ybWFsaXplZCA9IHRydWU7CgogICAgICAgICAgICAgICAgICAgIHVybCA9IGNvbnRleHQubmFtZVRvVXJsKG5vcm1hbGl6ZWROYW1lKTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQoKICAgICAgICAgICAgLy9JZiB0aGUgaWQgaXMgYSBwbHVnaW4gaWQgdGhhdCBjYW5ub3QgYmUgZGV0ZXJtaW5lZCBpZiBpdCBuZWVkcwogICAgICAgICAgICAvL25vcm1hbGl6YXRpb24sIHN0YW1wIGl0IHdpdGggYSB1bmlxdWUgSUQgc28gdHdvIG1hdGNoaW5nIHJlbGF0aXZlCiAgICAgICAgICAgIC8vaWRzIHRoYXQgbWF5IGNvbmZsaWN0IGNhbiBiZSBzZXBhcmF0ZS4KICAgICAgICAgICAgc3VmZml4ID0gcHJlZml4ICYmICFwbHVnaW5Nb2R1bGUgJiYgIWlzTm9ybWFsaXplZCA/CiAgICAgICAgICAgICAgICAgICAgICdfdW5ub3JtYWxpemVkJyArICh1bm5vcm1hbGl6ZWRDb3VudGVyICs9IDEpIDoKICAgICAgICAgICAgICAgICAgICAgJyc7CgogICAgICAgICAgICByZXR1cm4gewogICAgICAgICAgICAgICAgcHJlZml4OiBwcmVmaXgsCiAgICAgICAgICAgICAgICBuYW1lOiBub3JtYWxpemVkTmFtZSwKICAgICAgICAgICAgICAgIHBhcmVudE1hcDogcGFyZW50TW9kdWxlTWFwLAogICAgICAgICAgICAgICAgdW5ub3JtYWxpemVkOiAhIXN1ZmZpeCwKICAgICAgICAgICAgICAgIHVybDogdXJsLAogICAgICAgICAgICAgICAgb3JpZ2luYWxOYW1lOiBvcmlnaW5hbE5hbWUsCiAgICAgICAgICAgICAgICBpc0RlZmluZTogaXNEZWZpbmUsCiAgICAgICAgICAgICAgICBpZDogKHByZWZpeCA/CiAgICAgICAgICAgICAgICAgICAgICAgIHByZWZpeCArICchJyArIG5vcm1hbGl6ZWROYW1lIDoKICAgICAgICAgICAgICAgICAgICAgICAgbm9ybWFsaXplZE5hbWUpICsgc3VmZml4CiAgICAgICAgICAgIH07CiAgICAgICAgfQoKICAgICAgICBmdW5jdGlvbiBnZXRNb2R1bGUoZGVwTWFwKSB7CiAgICAgICAgICAgIHZhciBpZCA9IGRlcE1hcC5pZCwKICAgICAgICAgICAgICAgIG1vZCA9IGdldE93bihyZWdpc3RyeSwgaWQpOwoKICAgICAgICAgICAgaWYgKCFtb2QpIHsKICAgICAgICAgICAgICAgIG1vZCA9IHJlZ2lzdHJ5W2lkXSA9IG5ldyBjb250ZXh0Lk1vZHVsZShkZXBNYXApOwogICAgICAgICAgICB9CgogICAgICAgICAgICByZXR1cm4gbW9kOwogICAgICAgIH0KCiAgICAgICAgZnVuY3Rpb24gb24oZGVwTWFwLCBuYW1lLCBmbikgewogICAgICAgICAgICB2YXIgaWQgPSBkZXBNYXAuaWQsCiAgICAgICAgICAgICAgICBtb2QgPSBnZXRPd24ocmVnaXN0cnksIGlkKTsKCiAgICAgICAgICAgIGlmIChoYXNQcm9wKGRlZmluZWQsIGlkKSAmJgogICAgICAgICAgICAgICAgICAgICghbW9kIHx8IG1vZC5kZWZpbmVFbWl0Q29tcGxldGUpKSB7CiAgICAgICAgICAgICAgICBpZiAobmFtZSA9PT0gJ2RlZmluZWQnKSB7CiAgICAgICAgICAgICAgICAgICAgZm4oZGVmaW5lZFtpZF0pOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICAgICAgbW9kID0gZ2V0TW9kdWxlKGRlcE1hcCk7CiAgICAgICAgICAgICAgICBpZiAobW9kLmVycm9yICYmIG5hbWUgPT09ICdlcnJvcicpIHsKICAgICAgICAgICAgICAgICAgICBmbihtb2QuZXJyb3IpOwogICAgICAgICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgICAgICAgICBtb2Qub24obmFtZSwgZm4pOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CiAgICAgICAgfQoKICAgICAgICBmdW5jdGlvbiBvbkVycm9yKGVyciwgZXJyYmFjaykgewogICAgICAgICAgICB2YXIgaWRzID0gZXJyLnJlcXVpcmVNb2R1bGVzLAogICAgICAgICAgICAgICAgbm90aWZpZWQgPSBmYWxzZTsKCiAgICAgICAgICAgIGlmIChlcnJiYWNrKSB7CiAgICAgICAgICAgICAgICBlcnJiYWNrKGVycik7CiAgICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgICAgICBlYWNoKGlkcywgZnVuY3Rpb24gKGlkKSB7CiAgICAgICAgICAgICAgICAgICAgdmFyIG1vZCA9IGdldE93bihyZWdpc3RyeSwgaWQpOwogICAgICAgICAgICAgICAgICAgIGlmIChtb2QpIHsKICAgICAgICAgICAgICAgICAgICAgICAgLy9TZXQgZXJyb3Igb24gbW9kdWxlLCBzbyBpdCBza2lwcyB0aW1lb3V0IGNoZWNrcy4KICAgICAgICAgICAgICAgICAgICAgICAgbW9kLmVycm9yID0gZXJyOwogICAgICAgICAgICAgICAgICAgICAgICBpZiAobW9kLmV2ZW50cy5lcnJvcikgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgbm90aWZpZWQgPSB0cnVlOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9kLmVtaXQoJ2Vycm9yJywgZXJyKTsKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIH0pOwoKICAgICAgICAgICAgICAgIGlmICghbm90aWZpZWQpIHsKICAgICAgICAgICAgICAgICAgICByZXEub25FcnJvcihlcnIpOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CiAgICAgICAgfQoKICAgICAgICAvKioKICAgICAgICAgKiBJbnRlcm5hbCBtZXRob2QgdG8gdHJhbnNmZXIgZ2xvYmFsUXVldWUgaXRlbXMgdG8gdGhpcyBjb250ZXh0J3MKICAgICAgICAgKiBkZWZRdWV1ZS4KICAgICAgICAgKi8KICAgICAgICBmdW5jdGlvbiB0YWtlR2xvYmFsUXVldWUoKSB7CiAgICAgICAgICAgIC8vUHVzaCBhbGwgdGhlIGdsb2JhbERlZlF1ZXVlIGl0ZW1zIGludG8gdGhlIGNvbnRleHQncyBkZWZRdWV1ZQogICAgICAgICAgICBpZiAoZ2xvYmFsRGVmUXVldWUubGVuZ3RoKSB7CiAgICAgICAgICAgICAgICBlYWNoKGdsb2JhbERlZlF1ZXVlLCBmdW5jdGlvbihxdWV1ZUl0ZW0pIHsKICAgICAgICAgICAgICAgICAgICB2YXIgaWQgPSBxdWV1ZUl0ZW1bMF07CiAgICAgICAgICAgICAgICAgICAgaWYgKHR5cGVvZiBpZCA9PT0gJ3N0cmluZycpIHsKICAgICAgICAgICAgICAgICAgICAgICAgY29udGV4dC5kZWZRdWV1ZU1hcFtpZF0gPSB0cnVlOwogICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICBkZWZRdWV1ZS5wdXNoKHF1ZXVlSXRlbSk7CiAgICAgICAgICAgICAgICB9KTsKICAgICAgICAgICAgICAgIGdsb2JhbERlZlF1ZXVlID0gW107CiAgICAgICAgICAgIH0KICAgICAgICB9CgogICAgICAgIGhhbmRsZXJzID0gewogICAgICAgICAgICAncmVxdWlyZSc6IGZ1bmN0aW9uIChtb2QpIHsKICAgICAgICAgICAgICAgIGlmIChtb2QucmVxdWlyZSkgewogICAgICAgICAgICAgICAgICAgIHJldHVybiBtb2QucmVxdWlyZTsKICAgICAgICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIChtb2QucmVxdWlyZSA9IGNvbnRleHQubWFrZVJlcXVpcmUobW9kLm1hcCkpOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAnZXhwb3J0cyc6IGZ1bmN0aW9uIChtb2QpIHsKICAgICAgICAgICAgICAgIG1vZC51c2luZ0V4cG9ydHMgPSB0cnVlOwogICAgICAgICAgICAgICAgaWYgKG1vZC5tYXAuaXNEZWZpbmUpIHsKICAgICAgICAgICAgICAgICAgICBpZiAobW9kLmV4cG9ydHMpIHsKICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIChkZWZpbmVkW21vZC5tYXAuaWRdID0gbW9kLmV4cG9ydHMpOwogICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiAobW9kLmV4cG9ydHMgPSBkZWZpbmVkW21vZC5tYXAuaWRdID0ge30pOwogICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgJ21vZHVsZSc6IGZ1bmN0aW9uIChtb2QpIHsKICAgICAgICAgICAgICAgIGlmIChtb2QubW9kdWxlKSB7CiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIG1vZC5tb2R1bGU7CiAgICAgICAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICAgICAgICAgIHJldHVybiAobW9kLm1vZHVsZSA9IHsKICAgICAgICAgICAgICAgICAgICAgICAgaWQ6IG1vZC5tYXAuaWQsCiAgICAgICAgICAgICAgICAgICAgICAgIHVyaTogbW9kLm1hcC51cmwsCiAgICAgICAgICAgICAgICAgICAgICAgIGNvbmZpZzogZnVuY3Rpb24gKCkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGdldE93bihjb25maWcuY29uZmlnLCBtb2QubWFwLmlkKSB8fCB7fTsKICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgZXhwb3J0czogbW9kLmV4cG9ydHMgfHwgKG1vZC5leHBvcnRzID0ge30pCiAgICAgICAgICAgICAgICAgICAgfSk7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICB9OwoKICAgICAgICBmdW5jdGlvbiBjbGVhblJlZ2lzdHJ5KGlkKSB7CiAgICAgICAgICAgIC8vQ2xlYW4gdXAgbWFjaGluZXJ5IHVzZWQgZm9yIHdhaXRpbmcgbW9kdWxlcy4KICAgICAgICAgICAgZGVsZXRlIHJlZ2lzdHJ5W2lkXTsKICAgICAgICAgICAgZGVsZXRlIGVuYWJsZWRSZWdpc3RyeVtpZF07CiAgICAgICAgfQoKICAgICAgICBmdW5jdGlvbiBicmVha0N5Y2xlKG1vZCwgdHJhY2VkLCBwcm9jZXNzZWQpIHsKICAgICAgICAgICAgdmFyIGlkID0gbW9kLm1hcC5pZDsKCiAgICAgICAgICAgIGlmIChtb2QuZXJyb3IpIHsKICAgICAgICAgICAgICAgIG1vZC5lbWl0KCdlcnJvcicsIG1vZC5lcnJvcik7CiAgICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgICAgICB0cmFjZWRbaWRdID0gdHJ1ZTsKICAgICAgICAgICAgICAgIGVhY2gobW9kLmRlcE1hcHMsIGZ1bmN0aW9uIChkZXBNYXAsIGkpIHsKICAgICAgICAgICAgICAgICAgICB2YXIgZGVwSWQgPSBkZXBNYXAuaWQsCiAgICAgICAgICAgICAgICAgICAgICAgIGRlcCA9IGdldE93bihyZWdpc3RyeSwgZGVwSWQpOwoKICAgICAgICAgICAgICAgICAgICAvL09ubHkgZm9yY2UgdGhpbmdzIHRoYXQgaGF2ZSBub3QgY29tcGxldGVkCiAgICAgICAgICAgICAgICAgICAgLy9iZWluZyBkZWZpbmVkLCBzbyBzdGlsbCBpbiB0aGUgcmVnaXN0cnksCiAgICAgICAgICAgICAgICAgICAgLy9hbmQgb25seSBpZiBpdCBoYXMgbm90IGJlZW4gbWF0Y2hlZCB1cAogICAgICAgICAgICAgICAgICAgIC8vaW4gdGhlIG1vZHVsZSBhbHJlYWR5LgogICAgICAgICAgICAgICAgICAgIGlmIChkZXAgJiYgIW1vZC5kZXBNYXRjaGVkW2ldICYmICFwcm9jZXNzZWRbZGVwSWRdKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChnZXRPd24odHJhY2VkLCBkZXBJZCkpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1vZC5kZWZpbmVEZXAoaSwgZGVmaW5lZFtkZXBJZF0pOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9kLmNoZWNrKCk7IC8vcGFzcyBmYWxzZT8KICAgICAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrQ3ljbGUoZGVwLCB0cmFjZWQsIHByb2Nlc3NlZCk7CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICB9KTsKICAgICAgICAgICAgICAgIHByb2Nlc3NlZFtpZF0gPSB0cnVlOwogICAgICAgICAgICB9CiAgICAgICAgfQoKICAgICAgICBmdW5jdGlvbiBjaGVja0xvYWRlZCgpIHsKICAgICAgICAgICAgdmFyIGVyciwgdXNpbmdQYXRoRmFsbGJhY2ssCiAgICAgICAgICAgICAgICB3YWl0SW50ZXJ2YWwgPSBjb25maWcud2FpdFNlY29uZHMgKiAxMDAwLAogICAgICAgICAgICAgICAgLy9JdCBpcyBwb3NzaWJsZSB0byBkaXNhYmxlIHRoZSB3YWl0IGludGVydmFsIGJ5IHVzaW5nIHdhaXRTZWNvbmRzIG9mIDAuCiAgICAgICAgICAgICAgICBleHBpcmVkID0gd2FpdEludGVydmFsICYmIChjb250ZXh0LnN0YXJ0VGltZSArIHdhaXRJbnRlcnZhbCkgPCBuZXcgRGF0ZSgpLmdldFRpbWUoKSwKICAgICAgICAgICAgICAgIG5vTG9hZHMgPSBbXSwKICAgICAgICAgICAgICAgIHJlcUNhbGxzID0gW10sCiAgICAgICAgICAgICAgICBzdGlsbExvYWRpbmcgPSBmYWxzZSwKICAgICAgICAgICAgICAgIG5lZWRDeWNsZUNoZWNrID0gdHJ1ZTsKCiAgICAgICAgICAgIC8vRG8gbm90IGJvdGhlciBpZiB0aGlzIGNhbGwgd2FzIGEgcmVzdWx0IG9mIGEgY3ljbGUgYnJlYWsuCiAgICAgICAgICAgIGlmIChpbkNoZWNrTG9hZGVkKSB7CiAgICAgICAgICAgICAgICByZXR1cm47CiAgICAgICAgICAgIH0KCiAgICAgICAgICAgIGluQ2hlY2tMb2FkZWQgPSB0cnVlOwoKICAgICAgICAgICAgLy9GaWd1cmUgb3V0IHRoZSBzdGF0ZSBvZiBhbGwgdGhlIG1vZHVsZXMuCiAgICAgICAgICAgIGVhY2hQcm9wKGVuYWJsZWRSZWdpc3RyeSwgZnVuY3Rpb24gKG1vZCkgewogICAgICAgICAgICAgICAgdmFyIG1hcCA9IG1vZC5tYXAsCiAgICAgICAgICAgICAgICAgICAgbW9kSWQgPSBtYXAuaWQ7CgogICAgICAgICAgICAgICAgLy9Ta2lwIHRoaW5ncyB0aGF0IGFyZSBub3QgZW5hYmxlZCBvciBpbiBlcnJvciBzdGF0ZS4KICAgICAgICAgICAgICAgIGlmICghbW9kLmVuYWJsZWQpIHsKICAgICAgICAgICAgICAgICAgICByZXR1cm47CiAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgaWYgKCFtYXAuaXNEZWZpbmUpIHsKICAgICAgICAgICAgICAgICAgICByZXFDYWxscy5wdXNoKG1vZCk7CiAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgaWYgKCFtb2QuZXJyb3IpIHsKICAgICAgICAgICAgICAgICAgICAvL0lmIHRoZSBtb2R1bGUgc2hvdWxkIGJlIGV4ZWN1dGVkLCBhbmQgaXQgaGFzIG5vdAogICAgICAgICAgICAgICAgICAgIC8vYmVlbiBpbml0ZWQgYW5kIHRpbWUgaXMgdXAsIHJlbWVtYmVyIGl0LgogICAgICAgICAgICAgICAgICAgIGlmICghbW9kLmluaXRlZCAmJiBleHBpcmVkKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChoYXNQYXRoRmFsbGJhY2sobW9kSWQpKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB1c2luZ1BhdGhGYWxsYmFjayA9IHRydWU7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdGlsbExvYWRpbmcgPSB0cnVlOwogICAgICAgICAgICAgICAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgbm9Mb2Fkcy5wdXNoKG1vZElkKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlbW92ZVNjcmlwdChtb2RJZCk7CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKCFtb2QuaW5pdGVkICYmIG1vZC5mZXRjaGVkICYmIG1hcC5pc0RlZmluZSkgewogICAgICAgICAgICAgICAgICAgICAgICBzdGlsbExvYWRpbmcgPSB0cnVlOwogICAgICAgICAgICAgICAgICAgICAgICBpZiAoIW1hcC5wcmVmaXgpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vTm8gcmVhc29uIHRvIGtlZXAgbG9va2luZyBmb3IgdW5maW5pc2hlZAogICAgICAgICAgICAgICAgICAgICAgICAgICAgLy9sb2FkaW5nLiBJZiB0aGUgb25seSBzdGlsbExvYWRpbmcgaXMgYQogICAgICAgICAgICAgICAgICAgICAgICAgICAgLy9wbHVnaW4gcmVzb3VyY2UgdGhvdWdoLCBrZWVwIGdvaW5nLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgLy9iZWNhdXNlIGl0IG1heSBiZSB0aGF0IGEgcGx1Z2luIHJlc291cmNlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvL2lzIHdhaXRpbmcgb24gYSBub24tcGx1Z2luIGN5Y2xlLgogICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIChuZWVkQ3ljbGVDaGVjayA9IGZhbHNlKTsKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSk7CgogICAgICAgICAgICBpZiAoZXhwaXJlZCAmJiBub0xvYWRzLmxlbmd0aCkgewogICAgICAgICAgICAgICAgLy9JZiB3YWl0IHRpbWUgZXhwaXJlZCwgdGhyb3cgZXJyb3Igb2YgdW5sb2FkZWQgbW9kdWxlcy4KICAgICAgICAgICAgICAgIGVyciA9IG1ha2VFcnJvcigndGltZW91dCcsICdMb2FkIHRpbWVvdXQgZm9yIG1vZHVsZXM6ICcgKyBub0xvYWRzLCBudWxsLCBub0xvYWRzKTsKICAgICAgICAgICAgICAgIGVyci5jb250ZXh0TmFtZSA9IGNvbnRleHQuY29udGV4dE5hbWU7CiAgICAgICAgICAgICAgICByZXR1cm4gb25FcnJvcihlcnIpOwogICAgICAgICAgICB9CgogICAgICAgICAgICAvL05vdCBleHBpcmVkLCBjaGVjayBmb3IgYSBjeWNsZS4KICAgICAgICAgICAgaWYgKG5lZWRDeWNsZUNoZWNrKSB7CiAgICAgICAgICAgICAgICBlYWNoKHJlcUNhbGxzLCBmdW5jdGlvbiAobW9kKSB7CiAgICAgICAgICAgICAgICAgICAgYnJlYWtDeWNsZShtb2QsIHt9LCB7fSk7CiAgICAgICAgICAgICAgICB9KTsKICAgICAgICAgICAgfQoKICAgICAgICAgICAgLy9JZiBzdGlsbCB3YWl0aW5nIG9uIGxvYWRzLCBhbmQgdGhlIHdhaXRpbmcgbG9hZCBpcyBzb21ldGhpbmcKICAgICAgICAgICAgLy9vdGhlciB0aGFuIGEgcGx1Z2luIHJlc291cmNlLCBvciB0aGVyZSBhcmUgc3RpbGwgb3V0c3RhbmRpbmcKICAgICAgICAgICAgLy9zY3JpcHRzLCB0aGVuIGp1c3QgdHJ5IGJhY2sgbGF0ZXIuCiAgICAgICAgICAgIGlmICgoIWV4cGlyZWQgfHwgdXNpbmdQYXRoRmFsbGJhY2spICYmIHN0aWxsTG9hZGluZykgewogICAgICAgICAgICAgICAgLy9Tb21ldGhpbmcgaXMgc3RpbGwgd2FpdGluZyB0byBsb2FkLiBXYWl0IGZvciBpdCwgYnV0IG9ubHkKICAgICAgICAgICAgICAgIC8vaWYgYSB0aW1lb3V0IGlzIG5vdCBhbHJlYWR5IGluIGVmZmVjdC4KICAgICAgICAgICAgICAgIGlmICgoaXNCcm93c2VyIHx8IGlzV2ViV29ya2VyKSAmJiAhY2hlY2tMb2FkZWRUaW1lb3V0SWQpIHsKICAgICAgICAgICAgICAgICAgICBjaGVja0xvYWRlZFRpbWVvdXRJZCA9IHNldFRpbWVvdXQoZnVuY3Rpb24gKCkgewogICAgICAgICAgICAgICAgICAgICAgICBjaGVja0xvYWRlZFRpbWVvdXRJZCA9IDA7CiAgICAgICAgICAgICAgICAgICAgICAgIGNoZWNrTG9hZGVkKCk7CiAgICAgICAgICAgICAgICAgICAgfSwgNTApOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CgogICAgICAgICAgICBpbkNoZWNrTG9hZGVkID0gZmFsc2U7CiAgICAgICAgfQoKICAgICAgICBNb2R1bGUgPSBmdW5jdGlvbiAobWFwKSB7CiAgICAgICAgICAgIHRoaXMuZXZlbnRzID0gZ2V0T3duKHVuZGVmRXZlbnRzLCBtYXAuaWQpIHx8IHt9OwogICAgICAgICAgICB0aGlzLm1hcCA9IG1hcDsKICAgICAgICAgICAgdGhpcy5zaGltID0gZ2V0T3duKGNvbmZpZy5zaGltLCBtYXAuaWQpOwogICAgICAgICAgICB0aGlzLmRlcEV4cG9ydHMgPSBbXTsKICAgICAgICAgICAgdGhpcy5kZXBNYXBzID0gW107CiAgICAgICAgICAgIHRoaXMuZGVwTWF0Y2hlZCA9IFtdOwogICAgICAgICAgICB0aGlzLnBsdWdpbk1hcHMgPSB7fTsKICAgICAgICAgICAgdGhpcy5kZXBDb3VudCA9IDA7CgogICAgICAgICAgICAvKiB0aGlzLmV4cG9ydHMgdGhpcy5mYWN0b3J5CiAgICAgICAgICAgICAgIHRoaXMuZGVwTWFwcyA9IFtdLAogICAgICAgICAgICAgICB0aGlzLmVuYWJsZWQsIHRoaXMuZmV0Y2hlZAogICAgICAgICAgICAqLwogICAgICAgIH07CgogICAgICAgIE1vZHVsZS5wcm90b3R5cGUgPSB7CiAgICAgICAgICAgIGluaXQ6IGZ1bmN0aW9uIChkZXBNYXBzLCBmYWN0b3J5LCBlcnJiYWNrLCBvcHRpb25zKSB7CiAgICAgICAgICAgICAgICBvcHRpb25zID0gb3B0aW9ucyB8fCB7fTsKCiAgICAgICAgICAgICAgICAvL0RvIG5vdCBkbyBtb3JlIGluaXRzIGlmIGFscmVhZHkgZG9uZS4gQ2FuIGhhcHBlbiBpZiB0aGVyZQogICAgICAgICAgICAgICAgLy9hcmUgbXVsdGlwbGUgZGVmaW5lIGNhbGxzIGZvciB0aGUgc2FtZSBtb2R1bGUuIFRoYXQgaXMgbm90CiAgICAgICAgICAgICAgICAvL2Egbm9ybWFsLCBjb21tb24gY2FzZSwgYnV0IGl0IGlzIGFsc28gbm90IHVuZXhwZWN0ZWQuCiAgICAgICAgICAgICAgICBpZiAodGhpcy5pbml0ZWQpIHsKICAgICAgICAgICAgICAgICAgICByZXR1cm47CiAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgdGhpcy5mYWN0b3J5ID0gZmFjdG9yeTsKCiAgICAgICAgICAgICAgICBpZiAoZXJyYmFjaykgewogICAgICAgICAgICAgICAgICAgIC8vUmVnaXN0ZXIgZm9yIGVycm9ycyBvbiB0aGlzIG1vZHVsZS4KICAgICAgICAgICAgICAgICAgICB0aGlzLm9uKCdlcnJvcicsIGVycmJhY2spOwogICAgICAgICAgICAgICAgfSBlbHNlIGlmICh0aGlzLmV2ZW50cy5lcnJvcikgewogICAgICAgICAgICAgICAgICAgIC8vSWYgbm8gZXJyYmFjayBhbHJlYWR5LCBidXQgdGhlcmUgYXJlIGVycm9yIGxpc3RlbmVycwogICAgICAgICAgICAgICAgICAgIC8vb24gdGhpcyBtb2R1bGUsIHNldCB1cCBhbiBlcnJiYWNrIHRvIHBhc3MgdG8gdGhlIGRlcHMuCiAgICAgICAgICAgICAgICAgICAgZXJyYmFjayA9IGJpbmQodGhpcywgZnVuY3Rpb24gKGVycikgewogICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmVtaXQoJ2Vycm9yJywgZXJyKTsKICAgICAgICAgICAgICAgICAgICB9KTsKICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICAvL0RvIGEgY29weSBvZiB0aGUgZGVwZW5kZW5jeSBhcnJheSwgc28gdGhhdAogICAgICAgICAgICAgICAgLy9zb3VyY2UgaW5wdXRzIGFyZSBub3QgbW9kaWZpZWQuIEZvciBleGFtcGxlCiAgICAgICAgICAgICAgICAvLyJzaGltIiBkZXBzIGFyZSBwYXNzZWQgaW4gaGVyZSBkaXJlY3RseSwgYW5kCiAgICAgICAgICAgICAgICAvL2RvaW5nIGEgZGlyZWN0IG1vZGlmaWNhdGlvbiBvZiB0aGUgZGVwTWFwcyBhcnJheQogICAgICAgICAgICAgICAgLy93b3VsZCBhZmZlY3QgdGhhdCBjb25maWcuCiAgICAgICAgICAgICAgICB0aGlzLmRlcE1hcHMgPSBkZXBNYXBzICYmIGRlcE1hcHMuc2xpY2UoMCk7CgogICAgICAgICAgICAgICAgdGhpcy5lcnJiYWNrID0gZXJyYmFjazsKCiAgICAgICAgICAgICAgICAvL0luZGljYXRlIHRoaXMgbW9kdWxlIGhhcyBiZSBpbml0aWFsaXplZAogICAgICAgICAgICAgICAgdGhpcy5pbml0ZWQgPSB0cnVlOwoKICAgICAgICAgICAgICAgIHRoaXMuaWdub3JlID0gb3B0aW9ucy5pZ25vcmU7CgogICAgICAgICAgICAgICAgLy9Db3VsZCBoYXZlIG9wdGlvbiB0byBpbml0IHRoaXMgbW9kdWxlIGluIGVuYWJsZWQgbW9kZSwKICAgICAgICAgICAgICAgIC8vb3IgY291bGQgaGF2ZSBiZWVuIHByZXZpb3VzbHkgbWFya2VkIGFzIGVuYWJsZWQuIEhvd2V2ZXIsCiAgICAgICAgICAgICAgICAvL3RoZSBkZXBlbmRlbmNpZXMgYXJlIG5vdCBrbm93biB1bnRpbCBpbml0IGlzIGNhbGxlZC4gU28KICAgICAgICAgICAgICAgIC8vaWYgZW5hYmxlZCBwcmV2aW91c2x5LCBub3cgdHJpZ2dlciBkZXBlbmRlbmNpZXMgYXMgZW5hYmxlZC4KICAgICAgICAgICAgICAgIGlmIChvcHRpb25zLmVuYWJsZWQgfHwgdGhpcy5lbmFibGVkKSB7CiAgICAgICAgICAgICAgICAgICAgLy9FbmFibGUgdGhpcyBtb2R1bGUgYW5kIGRlcGVuZGVuY2llcy4KICAgICAgICAgICAgICAgICAgICAvL1dpbGwgY2FsbCB0aGlzLmNoZWNrKCkKICAgICAgICAgICAgICAgICAgICB0aGlzLmVuYWJsZSgpOwogICAgICAgICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgICAgICAgICB0aGlzLmNoZWNrKCk7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCgogICAgICAgICAgICBkZWZpbmVEZXA6IGZ1bmN0aW9uIChpLCBkZXBFeHBvcnRzKSB7CiAgICAgICAgICAgICAgICAvL0JlY2F1c2Ugb2YgY3ljbGVzLCBkZWZpbmVkIGNhbGxiYWNrIGZvciBhIGdpdmVuCiAgICAgICAgICAgICAgICAvL2V4cG9ydCBjYW4gYmUgY2FsbGVkIG1vcmUgdGhhbiBvbmNlLgogICAgICAgICAgICAgICAgaWYgKCF0aGlzLmRlcE1hdGNoZWRbaV0pIHsKICAgICAgICAgICAgICAgICAgICB0aGlzLmRlcE1hdGNoZWRbaV0gPSB0cnVlOwogICAgICAgICAgICAgICAgICAgIHRoaXMuZGVwQ291bnQgLT0gMTsKICAgICAgICAgICAgICAgICAgICB0aGlzLmRlcEV4cG9ydHNbaV0gPSBkZXBFeHBvcnRzOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAoKICAgICAgICAgICAgZmV0Y2g6IGZ1bmN0aW9uICgpIHsKICAgICAgICAgICAgICAgIGlmICh0aGlzLmZldGNoZWQpIHsKICAgICAgICAgICAgICAgICAgICByZXR1cm47CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICB0aGlzLmZldGNoZWQgPSB0cnVlOwoKICAgICAgICAgICAgICAgIGNvbnRleHQuc3RhcnRUaW1lID0gKG5ldyBEYXRlKCkpLmdldFRpbWUoKTsKCiAgICAgICAgICAgICAgICB2YXIgbWFwID0gdGhpcy5tYXA7CgogICAgICAgICAgICAgICAgLy9JZiB0aGUgbWFuYWdlciBpcyBmb3IgYSBwbHVnaW4gbWFuYWdlZCByZXNvdXJjZSwKICAgICAgICAgICAgICAgIC8vYXNrIHRoZSBwbHVnaW4gdG8gbG9hZCBpdCBub3cuCiAgICAgICAgICAgICAgICBpZiAodGhpcy5zaGltKSB7CiAgICAgICAgICAgICAgICAgICAgY29udGV4dC5tYWtlUmVxdWlyZSh0aGlzLm1hcCwgewogICAgICAgICAgICAgICAgICAgICAgICBlbmFibGVCdWlsZENhbGxiYWNrOiB0cnVlCiAgICAgICAgICAgICAgICAgICAgfSkodGhpcy5zaGltLmRlcHMgfHwgW10sIGJpbmQodGhpcywgZnVuY3Rpb24gKCkgewogICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gbWFwLnByZWZpeCA/IHRoaXMuY2FsbFBsdWdpbigpIDogdGhpcy5sb2FkKCk7CiAgICAgICAgICAgICAgICAgICAgfSkpOwogICAgICAgICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgICAgICAgICAvL1JlZ3VsYXIgZGVwZW5kZW5jeS4KICAgICAgICAgICAgICAgICAgICByZXR1cm4gbWFwLnByZWZpeCA/IHRoaXMuY2FsbFBsdWdpbigpIDogdGhpcy5sb2FkKCk7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCgogICAgICAgICAgICBsb2FkOiBmdW5jdGlvbiAoKSB7CiAgICAgICAgICAgICAgICB2YXIgdXJsID0gdGhpcy5tYXAudXJsOwoKICAgICAgICAgICAgICAgIC8vUmVndWxhciBkZXBlbmRlbmN5LgogICAgICAgICAgICAgICAgaWYgKCF1cmxGZXRjaGVkW3VybF0pIHsKICAgICAgICAgICAgICAgICAgICB1cmxGZXRjaGVkW3VybF0gPSB0cnVlOwogICAgICAgICAgICAgICAgICAgIGNvbnRleHQubG9hZCh0aGlzLm1hcC5pZCwgdXJsKTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKCiAgICAgICAgICAgIC8qKgogICAgICAgICAgICAgKiBDaGVja3MgaWYgdGhlIG1vZHVsZSBpcyByZWFkeSB0byBkZWZpbmUgaXRzZWxmLCBhbmQgaWYgc28sCiAgICAgICAgICAgICAqIGRlZmluZSBpdC4KICAgICAgICAgICAgICovCiAgICAgICAgICAgIGNoZWNrOiBmdW5jdGlvbiAoKSB7CiAgICAgICAgICAgICAgICBpZiAoIXRoaXMuZW5hYmxlZCB8fCB0aGlzLmVuYWJsaW5nKSB7CiAgICAgICAgICAgICAgICAgICAgcmV0dXJuOwogICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgIHZhciBlcnIsIGNqc01vZHVsZSwKICAgICAgICAgICAgICAgICAgICBpZCA9IHRoaXMubWFwLmlkLAogICAgICAgICAgICAgICAgICAgIGRlcEV4cG9ydHMgPSB0aGlzLmRlcEV4cG9ydHMsCiAgICAgICAgICAgICAgICAgICAgZXhwb3J0cyA9IHRoaXMuZXhwb3J0cywKICAgICAgICAgICAgICAgICAgICBmYWN0b3J5ID0gdGhpcy5mYWN0b3J5OwoKICAgICAgICAgICAgICAgIGlmICghdGhpcy5pbml0ZWQpIHsKICAgICAgICAgICAgICAgICAgICAvLyBPbmx5IGZldGNoIGlmIG5vdCBhbHJlYWR5IGluIHRoZSBkZWZRdWV1ZS4KICAgICAgICAgICAgICAgICAgICBpZiAoIWhhc1Byb3AoY29udGV4dC5kZWZRdWV1ZU1hcCwgaWQpKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuZmV0Y2goKTsKICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKHRoaXMuZXJyb3IpIHsKICAgICAgICAgICAgICAgICAgICB0aGlzLmVtaXQoJ2Vycm9yJywgdGhpcy5lcnJvcik7CiAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKCF0aGlzLmRlZmluaW5nKSB7CiAgICAgICAgICAgICAgICAgICAgLy9UaGUgZmFjdG9yeSBjb3VsZCB0cmlnZ2VyIGFub3RoZXIgcmVxdWlyZSBjYWxsCiAgICAgICAgICAgICAgICAgICAgLy90aGF0IHdvdWxkIHJlc3VsdCBpbiBjaGVja2luZyB0aGlzIG1vZHVsZSB0bwogICAgICAgICAgICAgICAgICAgIC8vZGVmaW5lIGl0c2VsZiBhZ2Fpbi4gSWYgYWxyZWFkeSBpbiB0aGUgcHJvY2VzcwogICAgICAgICAgICAgICAgICAgIC8vb2YgZG9pbmcgdGhhdCwgc2tpcCB0aGlzIHdvcmsuCiAgICAgICAgICAgICAgICAgICAgdGhpcy5kZWZpbmluZyA9IHRydWU7CgogICAgICAgICAgICAgICAgICAgIGlmICh0aGlzLmRlcENvdW50IDwgMSAmJiAhdGhpcy5kZWZpbmVkKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChpc0Z1bmN0aW9uKGZhY3RvcnkpKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0cnkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4cG9ydHMgPSBjb250ZXh0LmV4ZWNDYihpZCwgZmFjdG9yeSwgZGVwRXhwb3J0cywgZXhwb3J0cyk7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IGNhdGNoIChlKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZXJyID0gZTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBGYXZvciByZXR1cm4gdmFsdWUgb3ZlciBleHBvcnRzLiBJZiBub2RlL2NqcyBpbiBwbGF5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gdGhlbiB3aWxsIG5vdCBoYXZlIGEgcmV0dXJuIHZhbHVlIGFueXdheS4gRmF2b3IKICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIG1vZHVsZS5leHBvcnRzIGFzc2lnbm1lbnQgb3ZlciBleHBvcnRzIG9iamVjdC4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmICh0aGlzLm1hcC5pc0RlZmluZSAmJiBleHBvcnRzID09PSB1bmRlZmluZWQpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjanNNb2R1bGUgPSB0aGlzLm1vZHVsZTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAoY2pzTW9kdWxlKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4cG9ydHMgPSBjanNNb2R1bGUuZXhwb3J0czsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKHRoaXMudXNpbmdFeHBvcnRzKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vZXhwb3J0cyBhbHJlYWR5IHNldCB0aGUgZGVmaW5lZCB2YWx1ZS4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZXhwb3J0cyA9IHRoaXMuZXhwb3J0czsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGVycikgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIElmIHRoZXJlIGlzIGFuIGVycm9yIGxpc3RlbmVyLCBmYXZvciBwYXNzaW5nCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gdG8gdGhhdCBpbnN0ZWFkIG9mIHRocm93aW5nIGFuIGVycm9yLiBIb3dldmVyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIG9ubHkgZG8gaXQgZm9yIGRlZmluZSgpJ2QgIG1vZHVsZXMuIHJlcXVpcmUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBlcnJiYWNrcyBzaG91bGQgbm90IGJlIGNhbGxlZCBmb3IgZmFpbHVyZXMgaW4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyB0aGVpciBjYWxsYmFja3MgKCM2OTkpLiBIb3dldmVyIGlmIGEgZ2xvYmFsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gb25FcnJvciBpcyBzZXQsIHVzZSB0aGF0LgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmICgodGhpcy5ldmVudHMuZXJyb3IgJiYgdGhpcy5tYXAuaXNEZWZpbmUpIHx8CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcS5vbkVycm9yICE9PSBkZWZhdWx0T25FcnJvcikgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlcnIucmVxdWlyZU1hcCA9IHRoaXMubWFwOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlcnIucmVxdWlyZU1vZHVsZXMgPSB0aGlzLm1hcC5pc0RlZmluZSA/IFt0aGlzLm1hcC5pZF0gOiBudWxsOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlcnIucmVxdWlyZVR5cGUgPSB0aGlzLm1hcC5pc0RlZmluZSA/ICdkZWZpbmUnIDogJ3JlcXVpcmUnOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gb25FcnJvcigodGhpcy5lcnJvciA9IGVycikpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAodHlwZW9mIGNvbnNvbGUgIT09ICd1bmRlZmluZWQnICYmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIExvZyB0aGUgZXJyb3IgZm9yIGRlYnVnZ2luZy4gSWYgcHJvbWlzZXMgY291bGQgYmUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gdXNlZCwgdGhpcyB3b3VsZCBiZSBkaWZmZXJlbnQsIGJ1dCBtYWtpbmcgZG8uCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoZXJyKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBEbyBub3Qgd2FudCB0byBjb21wbGV0ZWx5IGxvc2UgdGhlIGVycm9yLiBXaGlsZSB0aGlzCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIHdpbGwgbWVzcyB1cCBwcm9jZXNzaW5nIGFuZCBsZWFkIHRvIHNpbWlsYXIgcmVzdWx0cwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBhcyBidWcgMTQ0MCwgaXQgYXQgbGVhc3Qgc3VyZmFjZXMgdGhlIGVycm9yLgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXEub25FcnJvcihlcnIpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vSnVzdCBhIGxpdGVyYWwgdmFsdWUKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4cG9ydHMgPSBmYWN0b3J5OwogICAgICAgICAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmV4cG9ydHMgPSBleHBvcnRzOwoKICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHRoaXMubWFwLmlzRGVmaW5lICYmICF0aGlzLmlnbm9yZSkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVmaW5lZFtpZF0gPSBleHBvcnRzOwoKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIChyZXEub25SZXNvdXJjZUxvYWQpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXIgcmVzTG9hZE1hcHMgPSBbXTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlYWNoKHRoaXMuZGVwTWFwcywgZnVuY3Rpb24gKGRlcE1hcCkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXNMb2FkTWFwcy5wdXNoKGRlcE1hcC5ub3JtYWxpemVkTWFwIHx8IGRlcE1hcCk7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSk7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVxLm9uUmVzb3VyY2VMb2FkKGNvbnRleHQsIHRoaXMubWFwLCByZXNMb2FkTWFwcyk7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICAgICAgICAgIC8vQ2xlYW4gdXAKICAgICAgICAgICAgICAgICAgICAgICAgY2xlYW5SZWdpc3RyeShpZCk7CgogICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmRlZmluZWQgPSB0cnVlOwogICAgICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICAgICAgLy9GaW5pc2hlZCB0aGUgZGVmaW5lIHN0YWdlLiBBbGxvdyBjYWxsaW5nIGNoZWNrIGFnYWluCiAgICAgICAgICAgICAgICAgICAgLy90byBhbGxvdyBkZWZpbmUgbm90aWZpY2F0aW9ucyBiZWxvdyBpbiB0aGUgY2FzZSBvZiBhCiAgICAgICAgICAgICAgICAgICAgLy9jeWNsZS4KICAgICAgICAgICAgICAgICAgICB0aGlzLmRlZmluaW5nID0gZmFsc2U7CgogICAgICAgICAgICAgICAgICAgIGlmICh0aGlzLmRlZmluZWQgJiYgIXRoaXMuZGVmaW5lRW1pdHRlZCkgewogICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmRlZmluZUVtaXR0ZWQgPSB0cnVlOwogICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmVtaXQoJ2RlZmluZWQnLCB0aGlzLmV4cG9ydHMpOwogICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmRlZmluZUVtaXRDb21wbGV0ZSA9IHRydWU7CiAgICAgICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKCiAgICAgICAgICAgIGNhbGxQbHVnaW46IGZ1bmN0aW9uICgpIHsKICAgICAgICAgICAgICAgIHZhciBtYXAgPSB0aGlzLm1hcCwKICAgICAgICAgICAgICAgICAgICBpZCA9IG1hcC5pZCwKICAgICAgICAgICAgICAgICAgICAvL01hcCBhbHJlYWR5IG5vcm1hbGl6ZWQgdGhlIHByZWZpeC4KICAgICAgICAgICAgICAgICAgICBwbHVnaW5NYXAgPSBtYWtlTW9kdWxlTWFwKG1hcC5wcmVmaXgpOwoKICAgICAgICAgICAgICAgIC8vTWFyayB0aGlzIGFzIGEgZGVwZW5kZW5jeSBmb3IgdGhpcyBwbHVnaW4sIHNvIGl0CiAgICAgICAgICAgICAgICAvL2NhbiBiZSB0cmFjZWQgZm9yIGN5Y2xlcy4KICAgICAgICAgICAgICAgIHRoaXMuZGVwTWFwcy5wdXNoKHBsdWdpbk1hcCk7CgogICAgICAgICAgICAgICAgb24ocGx1Z2luTWFwLCAnZGVmaW5lZCcsIGJpbmQodGhpcywgZnVuY3Rpb24gKHBsdWdpbikgewogICAgICAgICAgICAgICAgICAgIHZhciBsb2FkLCBub3JtYWxpemVkTWFwLCBub3JtYWxpemVkTW9kLAogICAgICAgICAgICAgICAgICAgICAgICBidW5kbGVJZCA9IGdldE93bihidW5kbGVzTWFwLCB0aGlzLm1hcC5pZCksCiAgICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSB0aGlzLm1hcC5uYW1lLAogICAgICAgICAgICAgICAgICAgICAgICBwYXJlbnROYW1lID0gdGhpcy5tYXAucGFyZW50TWFwID8gdGhpcy5tYXAucGFyZW50TWFwLm5hbWUgOiBudWxsLAogICAgICAgICAgICAgICAgICAgICAgICBsb2NhbFJlcXVpcmUgPSBjb250ZXh0Lm1ha2VSZXF1aXJlKG1hcC5wYXJlbnRNYXAsIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVuYWJsZUJ1aWxkQ2FsbGJhY2s6IHRydWUKICAgICAgICAgICAgICAgICAgICAgICAgfSk7CgogICAgICAgICAgICAgICAgICAgIC8vSWYgY3VycmVudCBtYXAgaXMgbm90IG5vcm1hbGl6ZWQsIHdhaXQgZm9yIHRoYXQKICAgICAgICAgICAgICAgICAgICAvL25vcm1hbGl6ZWQgbmFtZSB0byBsb2FkIGluc3RlYWQgb2YgY29udGludWluZy4KICAgICAgICAgICAgICAgICAgICBpZiAodGhpcy5tYXAudW5ub3JtYWxpemVkKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIC8vTm9ybWFsaXplIHRoZSBJRCBpZiB0aGUgcGx1Z2luIGFsbG93cyBpdC4KICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHBsdWdpbi5ub3JtYWxpemUpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSBwbHVnaW4ubm9ybWFsaXplKG5hbWUsIGZ1bmN0aW9uIChuYW1lKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIG5vcm1hbGl6ZShuYW1lLCBwYXJlbnROYW1lLCB0cnVlKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0pIHx8ICcnOwogICAgICAgICAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgICAgICAgICAvL3ByZWZpeCBhbmQgbmFtZSBzaG91bGQgYWxyZWFkeSBiZSBub3JtYWxpemVkLCBubyBuZWVkCiAgICAgICAgICAgICAgICAgICAgICAgIC8vZm9yIGFwcGx5aW5nIG1hcCBjb25maWcgYWdhaW4gZWl0aGVyLgogICAgICAgICAgICAgICAgICAgICAgICBub3JtYWxpemVkTWFwID0gbWFrZU1vZHVsZU1hcChtYXAucHJlZml4ICsgJyEnICsgbmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5tYXAucGFyZW50TWFwKTsKICAgICAgICAgICAgICAgICAgICAgICAgb24obm9ybWFsaXplZE1hcCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICdkZWZpbmVkJywgYmluZCh0aGlzLCBmdW5jdGlvbiAodmFsdWUpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGlzLm1hcC5ub3JtYWxpemVkTWFwID0gbm9ybWFsaXplZE1hcDsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmluaXQoW10sIGZ1bmN0aW9uICgpIHsgcmV0dXJuIHZhbHVlOyB9LCBudWxsLCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVuYWJsZWQ6IHRydWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlnbm9yZTogdHJ1ZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0pOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgfSkpOwoKICAgICAgICAgICAgICAgICAgICAgICAgbm9ybWFsaXplZE1vZCA9IGdldE93bihyZWdpc3RyeSwgbm9ybWFsaXplZE1hcC5pZCk7CiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChub3JtYWxpemVkTW9kKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvL01hcmsgdGhpcyBhcyBhIGRlcGVuZGVuY3kgZm9yIHRoaXMgcGx1Z2luLCBzbyBpdAogICAgICAgICAgICAgICAgICAgICAgICAgICAgLy9jYW4gYmUgdHJhY2VkIGZvciBjeWNsZXMuCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmRlcE1hcHMucHVzaChub3JtYWxpemVkTWFwKTsKCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAodGhpcy5ldmVudHMuZXJyb3IpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBub3JtYWxpemVkTW9kLm9uKCdlcnJvcicsIGJpbmQodGhpcywgZnVuY3Rpb24gKGVycikgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmVtaXQoJ2Vycm9yJywgZXJyKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9KSk7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBub3JtYWxpemVkTW9kLmVuYWJsZSgpOwogICAgICAgICAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgICAgICAgICByZXR1cm47CiAgICAgICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgICAgICAvL0lmIGEgcGF0aHMgY29uZmlnLCB0aGVuIGp1c3QgbG9hZCB0aGF0IGZpbGUgaW5zdGVhZCB0bwogICAgICAgICAgICAgICAgICAgIC8vcmVzb2x2ZSB0aGUgcGx1Z2luLCBhcyBpdCBpcyBidWlsdCBpbnRvIHRoYXQgcGF0aHMgbGF5ZXIuCiAgICAgICAgICAgICAgICAgICAgaWYgKGJ1bmRsZUlkKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMubWFwLnVybCA9IGNvbnRleHQubmFtZVRvVXJsKGJ1bmRsZUlkKTsKICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5sb2FkKCk7CiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybjsKICAgICAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgICAgIGxvYWQgPSBiaW5kKHRoaXMsIGZ1bmN0aW9uICh2YWx1ZSkgewogICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmluaXQoW10sIGZ1bmN0aW9uICgpIHsgcmV0dXJuIHZhbHVlOyB9LCBudWxsLCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbmFibGVkOiB0cnVlCiAgICAgICAgICAgICAgICAgICAgICAgIH0pOwogICAgICAgICAgICAgICAgICAgIH0pOwoKICAgICAgICAgICAgICAgICAgICBsb2FkLmVycm9yID0gYmluZCh0aGlzLCBmdW5jdGlvbiAoZXJyKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuaW5pdGVkID0gdHJ1ZTsKICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5lcnJvciA9IGVycjsKICAgICAgICAgICAgICAgICAgICAgICAgZXJyLnJlcXVpcmVNb2R1bGVzID0gW2lkXTsKCiAgICAgICAgICAgICAgICAgICAgICAgIC8vUmVtb3ZlIHRlbXAgdW5ub3JtYWxpemVkIG1vZHVsZXMgZm9yIHRoaXMgbW9kdWxlLAogICAgICAgICAgICAgICAgICAgICAgICAvL3NpbmNlIHRoZXkgd2lsbCBuZXZlciBiZSByZXNvbHZlZCBvdGhlcndpc2Ugbm93LgogICAgICAgICAgICAgICAgICAgICAgICBlYWNoUHJvcChyZWdpc3RyeSwgZnVuY3Rpb24gKG1vZCkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKG1vZC5tYXAuaWQuaW5kZXhPZihpZCArICdfdW5ub3JtYWxpemVkJykgPT09IDApIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbGVhblJlZ2lzdHJ5KG1vZC5tYXAuaWQpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9KTsKCiAgICAgICAgICAgICAgICAgICAgICAgIG9uRXJyb3IoZXJyKTsKICAgICAgICAgICAgICAgICAgICB9KTsKCiAgICAgICAgICAgICAgICAgICAgLy9BbGxvdyBwbHVnaW5zIHRvIGxvYWQgb3RoZXIgY29kZSB3aXRob3V0IGhhdmluZyB0byBrbm93IHRoZQogICAgICAgICAgICAgICAgICAgIC8vY29udGV4dCBvciBob3cgdG8gJ2NvbXBsZXRlJyB0aGUgbG9hZC4KICAgICAgICAgICAgICAgICAgICBsb2FkLmZyb21UZXh0ID0gYmluZCh0aGlzLCBmdW5jdGlvbiAodGV4dCwgdGV4dEFsdCkgewogICAgICAgICAgICAgICAgICAgICAgICAvKmpzbGludCBldmlsOiB0cnVlICovCiAgICAgICAgICAgICAgICAgICAgICAgIHZhciBtb2R1bGVOYW1lID0gbWFwLm5hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtb2R1bGVNYXAgPSBtYWtlTW9kdWxlTWFwKG1vZHVsZU5hbWUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgaGFzSW50ZXJhY3RpdmUgPSB1c2VJbnRlcmFjdGl2ZTsKCiAgICAgICAgICAgICAgICAgICAgICAgIC8vQXMgb2YgMi4xLjAsIHN1cHBvcnQganVzdCBwYXNzaW5nIHRoZSB0ZXh0LCB0byByZWluZm9yY2UKICAgICAgICAgICAgICAgICAgICAgICAgLy9mcm9tVGV4dCBvbmx5IGJlaW5nIGNhbGxlZCBvbmNlIHBlciByZXNvdXJjZS4gU3RpbGwKICAgICAgICAgICAgICAgICAgICAgICAgLy9zdXBwb3J0IG9sZCBzdHlsZSBvZiBwYXNzaW5nIG1vZHVsZU5hbWUgYnV0IGRpc2NhcmQKICAgICAgICAgICAgICAgICAgICAgICAgLy90aGF0IG1vZHVsZU5hbWUgaW4gZmF2b3Igb2YgdGhlIGludGVybmFsIHJlZi4KICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHRleHRBbHQpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRleHQgPSB0ZXh0QWx0OwogICAgICAgICAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgICAgICAgICAvL1R1cm4gb2ZmIGludGVyYWN0aXZlIHNjcmlwdCBtYXRjaGluZyBmb3IgSUUgZm9yIGFueSBkZWZpbmUKICAgICAgICAgICAgICAgICAgICAgICAgLy9jYWxscyBpbiB0aGUgdGV4dCwgdGhlbiB0dXJuIGl0IGJhY2sgb24gYXQgdGhlIGVuZC4KICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGhhc0ludGVyYWN0aXZlKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB1c2VJbnRlcmFjdGl2ZSA9IGZhbHNlOwogICAgICAgICAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgICAgICAgICAvL1ByaW1lIHRoZSBzeXN0ZW0gYnkgY3JlYXRpbmcgYSBtb2R1bGUgaW5zdGFuY2UgZm9yCiAgICAgICAgICAgICAgICAgICAgICAgIC8vaXQuCiAgICAgICAgICAgICAgICAgICAgICAgIGdldE1vZHVsZShtb2R1bGVNYXApOwoKICAgICAgICAgICAgICAgICAgICAgICAgLy9UcmFuc2ZlciBhbnkgY29uZmlnIHRvIHRoaXMgb3RoZXIgbW9kdWxlLgogICAgICAgICAgICAgICAgICAgICAgICBpZiAoaGFzUHJvcChjb25maWcuY29uZmlnLCBpZCkpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbmZpZy5jb25maWdbbW9kdWxlTmFtZV0gPSBjb25maWcuY29uZmlnW2lkXTsKICAgICAgICAgICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgICAgICAgICAgdHJ5IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcS5leGVjKHRleHQpOwogICAgICAgICAgICAgICAgICAgICAgICB9IGNhdGNoIChlKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gb25FcnJvcihtYWtlRXJyb3IoJ2Zyb210ZXh0ZXZhbCcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdmcm9tVGV4dCBldmFsIGZvciAnICsgaWQgKwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICcgZmFpbGVkOiAnICsgZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgW2lkXSkpOwogICAgICAgICAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgICAgICAgICBpZiAoaGFzSW50ZXJhY3RpdmUpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVzZUludGVyYWN0aXZlID0gdHJ1ZTsKICAgICAgICAgICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgICAgICAgICAgLy9NYXJrIHRoaXMgYXMgYSBkZXBlbmRlbmN5IGZvciB0aGUgcGx1Z2luCiAgICAgICAgICAgICAgICAgICAgICAgIC8vcmVzb3VyY2UKICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5kZXBNYXBzLnB1c2gobW9kdWxlTWFwKTsKCiAgICAgICAgICAgICAgICAgICAgICAgIC8vU3VwcG9ydCBhbm9ueW1vdXMgbW9kdWxlcy4KICAgICAgICAgICAgICAgICAgICAgICAgY29udGV4dC5jb21wbGV0ZUxvYWQobW9kdWxlTmFtZSk7CgogICAgICAgICAgICAgICAgICAgICAgICAvL0JpbmQgdGhlIHZhbHVlIG9mIHRoYXQgbW9kdWxlIHRvIHRoZSB2YWx1ZSBmb3IgdGhpcwogICAgICAgICAgICAgICAgICAgICAgICAvL3Jlc291cmNlIElELgogICAgICAgICAgICAgICAgICAgICAgICBsb2NhbFJlcXVpcmUoW21vZHVsZU5hbWVdLCBsb2FkKTsKICAgICAgICAgICAgICAgICAgICB9KTsKCiAgICAgICAgICAgICAgICAgICAgLy9Vc2UgcGFyZW50TmFtZSBoZXJlIHNpbmNlIHRoZSBwbHVnaW4ncyBuYW1lIGlzIG5vdCByZWxpYWJsZSwKICAgICAgICAgICAgICAgICAgICAvL2NvdWxkIGJlIHNvbWUgd2VpcmQgc3RyaW5nIHdpdGggbm8gcGF0aCB0aGF0IGFjdHVhbGx5IHdhbnRzIHRvCiAgICAgICAgICAgICAgICAgICAgLy9yZWZlcmVuY2UgdGhlIHBhcmVudE5hbWUncyBwYXRoLgogICAgICAgICAgICAgICAgICAgIHBsdWdpbi5sb2FkKG1hcC5uYW1lLCBsb2NhbFJlcXVpcmUsIGxvYWQsIGNvbmZpZyk7CiAgICAgICAgICAgICAgICB9KSk7CgogICAgICAgICAgICAgICAgY29udGV4dC5lbmFibGUocGx1Z2luTWFwLCB0aGlzKTsKICAgICAgICAgICAgICAgIHRoaXMucGx1Z2luTWFwc1twbHVnaW5NYXAuaWRdID0gcGx1Z2luTWFwOwogICAgICAgICAgICB9LAoKICAgICAgICAgICAgZW5hYmxlOiBmdW5jdGlvbiAoKSB7CiAgICAgICAgICAgICAgICBlbmFibGVkUmVnaXN0cnlbdGhpcy5tYXAuaWRdID0gdGhpczsKICAgICAgICAgICAgICAgIHRoaXMuZW5hYmxlZCA9IHRydWU7CgogICAgICAgICAgICAgICAgLy9TZXQgZmxhZyBtZW50aW9uaW5nIHRoYXQgdGhlIG1vZHVsZSBpcyBlbmFibGluZywKICAgICAgICAgICAgICAgIC8vc28gdGhhdCBpbW1lZGlhdGUgY2FsbHMgdG8gdGhlIGRlZmluZWQgY2FsbGJhY2tzCiAgICAgICAgICAgICAgICAvL2ZvciBkZXBlbmRlbmNpZXMgZG8gbm90IHRyaWdnZXIgaW5hZHZlcnRlbnQgbG9hZAogICAgICAgICAgICAgICAgLy93aXRoIHRoZSBkZXBDb3VudCBzdGlsbCBiZWluZyB6ZXJvLgogICAgICAgICAgICAgICAgdGhpcy5lbmFibGluZyA9IHRydWU7CgogICAgICAgICAgICAgICAgLy9FbmFibGUgZWFjaCBkZXBlbmRlbmN5CiAgICAgICAgICAgICAgICBlYWNoKHRoaXMuZGVwTWFwcywgYmluZCh0aGlzLCBmdW5jdGlvbiAoZGVwTWFwLCBpKSB7CiAgICAgICAgICAgICAgICAgICAgdmFyIGlkLCBtb2QsIGhhbmRsZXI7CgogICAgICAgICAgICAgICAgICAgIGlmICh0eXBlb2YgZGVwTWFwID09PSAnc3RyaW5nJykgewogICAgICAgICAgICAgICAgICAgICAgICAvL0RlcGVuZGVuY3kgbmVlZHMgdG8gYmUgY29udmVydGVkIHRvIGEgZGVwTWFwCiAgICAgICAgICAgICAgICAgICAgICAgIC8vYW5kIHdpcmVkIHVwIHRvIHRoaXMgbW9kdWxlLgogICAgICAgICAgICAgICAgICAgICAgICBkZXBNYXAgPSBtYWtlTW9kdWxlTWFwKGRlcE1hcCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAodGhpcy5tYXAuaXNEZWZpbmUgPyB0aGlzLm1hcCA6IHRoaXMubWFwLnBhcmVudE1hcCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFsc2UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIXRoaXMuc2tpcE1hcCk7CiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuZGVwTWFwc1tpXSA9IGRlcE1hcDsKCiAgICAgICAgICAgICAgICAgICAgICAgIGhhbmRsZXIgPSBnZXRPd24oaGFuZGxlcnMsIGRlcE1hcC5pZCk7CgogICAgICAgICAgICAgICAgICAgICAgICBpZiAoaGFuZGxlcikgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5kZXBFeHBvcnRzW2ldID0gaGFuZGxlcih0aGlzKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybjsKICAgICAgICAgICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5kZXBDb3VudCArPSAxOwoKICAgICAgICAgICAgICAgICAgICAgICAgb24oZGVwTWFwLCAnZGVmaW5lZCcsIGJpbmQodGhpcywgZnVuY3Rpb24gKGRlcEV4cG9ydHMpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmICh0aGlzLnVuZGVmZWQpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm47CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmRlZmluZURlcChpLCBkZXBFeHBvcnRzKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuY2hlY2soKTsKICAgICAgICAgICAgICAgICAgICAgICAgfSkpOwoKICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHRoaXMuZXJyYmFjaykgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgb24oZGVwTWFwLCAnZXJyb3InLCBiaW5kKHRoaXMsIHRoaXMuZXJyYmFjaykpOwogICAgICAgICAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKHRoaXMuZXZlbnRzLmVycm9yKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBObyBkaXJlY3QgZXJyYmFjayBvbiB0aGlzIG1vZHVsZSwgYnV0IHNvbWV0aGluZwogICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gZWxzZSBpcyBsaXN0ZW5pbmcgZm9yIGVycm9ycywgc28gYmUgc3VyZSB0bwogICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gcHJvcGFnYXRlIHRoZSBlcnJvciBjb3JyZWN0bHkuCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBvbihkZXBNYXAsICdlcnJvcicsIGJpbmQodGhpcywgZnVuY3Rpb24oZXJyKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5lbWl0KCdlcnJvcicsIGVycik7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9KSk7CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgICAgIGlkID0gZGVwTWFwLmlkOwogICAgICAgICAgICAgICAgICAgIG1vZCA9IHJlZ2lzdHJ5W2lkXTsKCiAgICAgICAgICAgICAgICAgICAgLy9Ta2lwIHNwZWNpYWwgbW9kdWxlcyBsaWtlICdyZXF1aXJlJywgJ2V4cG9ydHMnLCAnbW9kdWxlJwogICAgICAgICAgICAgICAgICAgIC8vQWxzbywgZG9uJ3QgY2FsbCBlbmFibGUgaWYgaXQgaXMgYWxyZWFkeSBlbmFibGVkLAogICAgICAgICAgICAgICAgICAgIC8vaW1wb3J0YW50IGluIGNpcmN1bGFyIGRlcGVuZGVuY3kgY2FzZXMuCiAgICAgICAgICAgICAgICAgICAgaWYgKCFoYXNQcm9wKGhhbmRsZXJzLCBpZCkgJiYgbW9kICYmICFtb2QuZW5hYmxlZCkgewogICAgICAgICAgICAgICAgICAgICAgICBjb250ZXh0LmVuYWJsZShkZXBNYXAsIHRoaXMpOwogICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIH0pKTsKCiAgICAgICAgICAgICAgICAvL0VuYWJsZSBlYWNoIHBsdWdpbiB0aGF0IGlzIHVzZWQgaW4KICAgICAgICAgICAgICAgIC8vYSBkZXBlbmRlbmN5CiAgICAgICAgICAgICAgICBlYWNoUHJvcCh0aGlzLnBsdWdpbk1hcHMsIGJpbmQodGhpcywgZnVuY3Rpb24gKHBsdWdpbk1hcCkgewogICAgICAgICAgICAgICAgICAgIHZhciBtb2QgPSBnZXRPd24ocmVnaXN0cnksIHBsdWdpbk1hcC5pZCk7CiAgICAgICAgICAgICAgICAgICAgaWYgKG1vZCAmJiAhbW9kLmVuYWJsZWQpIHsKICAgICAgICAgICAgICAgICAgICAgICAgY29udGV4dC5lbmFibGUocGx1Z2luTWFwLCB0aGlzKTsKICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICB9KSk7CgogICAgICAgICAgICAgICAgdGhpcy5lbmFibGluZyA9IGZhbHNlOwoKICAgICAgICAgICAgICAgIHRoaXMuY2hlY2soKTsKICAgICAgICAgICAgfSwKCiAgICAgICAgICAgIG9uOiBmdW5jdGlvbiAobmFtZSwgY2IpIHsKICAgICAgICAgICAgICAgIHZhciBjYnMgPSB0aGlzLmV2ZW50c1tuYW1lXTsKICAgICAgICAgICAgICAgIGlmICghY2JzKSB7CiAgICAgICAgICAgICAgICAgICAgY2JzID0gdGhpcy5ldmVudHNbbmFtZV0gPSBbXTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIGNicy5wdXNoKGNiKTsKICAgICAgICAgICAgfSwKCiAgICAgICAgICAgIGVtaXQ6IGZ1bmN0aW9uIChuYW1lLCBldnQpIHsKICAgICAgICAgICAgICAgIGVhY2godGhpcy5ldmVudHNbbmFtZV0sIGZ1bmN0aW9uIChjYikgewogICAgICAgICAgICAgICAgICAgIGNiKGV2dCk7CiAgICAgICAgICAgICAgICB9KTsKICAgICAgICAgICAgICAgIGlmIChuYW1lID09PSAnZXJyb3InKSB7CiAgICAgICAgICAgICAgICAgICAgLy9Ob3cgdGhhdCB0aGUgZXJyb3IgaGFuZGxlciB3YXMgdHJpZ2dlcmVkLCByZW1vdmUKICAgICAgICAgICAgICAgICAgICAvL3RoZSBsaXN0ZW5lcnMsIHNpbmNlIHRoaXMgYnJva2VuIE1vZHVsZSBpbnN0YW5jZQogICAgICAgICAgICAgICAgICAgIC8vY2FuIHN0YXkgYXJvdW5kIGZvciBhIHdoaWxlIGluIHRoZSByZWdpc3RyeS4KICAgICAgICAgICAgICAgICAgICBkZWxldGUgdGhpcy5ldmVudHNbbmFtZV07CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICB9OwoKICAgICAgICBmdW5jdGlvbiBjYWxsR2V0TW9kdWxlKGFyZ3MpIHsKICAgICAgICAgICAgLy9Ta2lwIG1vZHVsZXMgYWxyZWFkeSBkZWZpbmVkLgogICAgICAgICAgICBpZiAoIWhhc1Byb3AoZGVmaW5lZCwgYXJnc1swXSkpIHsKICAgICAgICAgICAgICAgIGdldE1vZHVsZShtYWtlTW9kdWxlTWFwKGFyZ3NbMF0sIG51bGwsIHRydWUpKS5pbml0KGFyZ3NbMV0sIGFyZ3NbMl0pOwogICAgICAgICAgICB9CiAgICAgICAgfQoKICAgICAgICBmdW5jdGlvbiByZW1vdmVMaXN0ZW5lcihub2RlLCBmdW5jLCBuYW1lLCBpZU5hbWUpIHsKICAgICAgICAgICAgLy9GYXZvciBkZXRhY2hFdmVudCBiZWNhdXNlIG9mIElFOQogICAgICAgICAgICAvL2lzc3VlLCBzZWUgYXR0YWNoRXZlbnQvYWRkRXZlbnRMaXN0ZW5lciBjb21tZW50IGVsc2V3aGVyZQogICAgICAgICAgICAvL2luIHRoaXMgZmlsZS4KICAgICAgICAgICAgaWYgKG5vZGUuZGV0YWNoRXZlbnQgJiYgIWlzT3BlcmEpIHsKICAgICAgICAgICAgICAgIC8vUHJvYmFibHkgSUUuIElmIG5vdCBpdCB3aWxsIHRocm93IGFuIGVycm9yLCB3aGljaCB3aWxsIGJlCiAgICAgICAgICAgICAgICAvL3VzZWZ1bCB0byBrbm93LgogICAgICAgICAgICAgICAgaWYgKGllTmFtZSkgewogICAgICAgICAgICAgICAgICAgIG5vZGUuZGV0YWNoRXZlbnQoaWVOYW1lLCBmdW5jKTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgICAgIG5vZGUucmVtb3ZlRXZlbnRMaXN0ZW5lcihuYW1lLCBmdW5jLCBmYWxzZSk7CiAgICAgICAgICAgIH0KICAgICAgICB9CgogICAgICAgIC8qKgogICAgICAgICAqIEdpdmVuIGFuIGV2ZW50IGZyb20gYSBzY3JpcHQgbm9kZSwgZ2V0IHRoZSByZXF1aXJlanMgaW5mbyBmcm9tIGl0LAogICAgICAgICAqIGFuZCB0aGVuIHJlbW92ZXMgdGhlIGV2ZW50IGxpc3RlbmVycyBvbiB0aGUgbm9kZS4KICAgICAgICAgKiBAcGFyYW0ge0V2ZW50fSBldnQKICAgICAgICAgKiBAcmV0dXJucyB7T2JqZWN0fQogICAgICAgICAqLwogICAgICAgIGZ1bmN0aW9uIGdldFNjcmlwdERhdGEoZXZ0KSB7CiAgICAgICAgICAgIC8vVXNpbmcgY3VycmVudFRhcmdldCBpbnN0ZWFkIG9mIHRhcmdldCBmb3IgRmlyZWZveCAyLjAncyBzYWtlLiBOb3QKICAgICAgICAgICAgLy9hbGwgb2xkIGJyb3dzZXJzIHdpbGwgYmUgc3VwcG9ydGVkLCBidXQgdGhpcyBvbmUgd2FzIGVhc3kgZW5vdWdoCiAgICAgICAgICAgIC8vdG8gc3VwcG9ydCBhbmQgc3RpbGwgbWFrZXMgc2Vuc2UuCiAgICAgICAgICAgIHZhciBub2RlID0gZXZ0LmN1cnJlbnRUYXJnZXQgfHwgZXZ0LnNyY0VsZW1lbnQ7CgogICAgICAgICAgICAvL1JlbW92ZSB0aGUgbGlzdGVuZXJzIG9uY2UgaGVyZS4KICAgICAgICAgICAgcmVtb3ZlTGlzdGVuZXIobm9kZSwgY29udGV4dC5vblNjcmlwdExvYWQsICdsb2FkJywgJ29ucmVhZHlzdGF0ZWNoYW5nZScpOwogICAgICAgICAgICByZW1vdmVMaXN0ZW5lcihub2RlLCBjb250ZXh0Lm9uU2NyaXB0RXJyb3IsICdlcnJvcicpOwoKICAgICAgICAgICAgcmV0dXJuIHsKICAgICAgICAgICAgICAgIG5vZGU6IG5vZGUsCiAgICAgICAgICAgICAgICBpZDogbm9kZSAmJiBub2RlLmdldEF0dHJpYnV0ZSgnZGF0YS1yZXF1aXJlbW9kdWxlJykKICAgICAgICAgICAgfTsKICAgICAgICB9CgogICAgICAgIGZ1bmN0aW9uIGludGFrZURlZmluZXMoKSB7CiAgICAgICAgICAgIHZhciBhcmdzOwoKICAgICAgICAgICAgLy9BbnkgZGVmaW5lZCBtb2R1bGVzIGluIHRoZSBnbG9iYWwgcXVldWUsIGludGFrZSB0aGVtIG5vdy4KICAgICAgICAgICAgdGFrZUdsb2JhbFF1ZXVlKCk7CgogICAgICAgICAgICAvL01ha2Ugc3VyZSBhbnkgcmVtYWluaW5nIGRlZlF1ZXVlIGl0ZW1zIGdldCBwcm9wZXJseSBwcm9jZXNzZWQuCiAgICAgICAgICAgIHdoaWxlIChkZWZRdWV1ZS5sZW5ndGgpIHsKICAgICAgICAgICAgICAgIGFyZ3MgPSBkZWZRdWV1ZS5zaGlmdCgpOwogICAgICAgICAgICAgICAgaWYgKGFyZ3NbMF0gPT09IG51bGwpIHsKICAgICAgICAgICAgICAgICAgICByZXR1cm4gb25FcnJvcihtYWtlRXJyb3IoJ21pc21hdGNoJywgJ01pc21hdGNoZWQgYW5vbnltb3VzIGRlZmluZSgpIG1vZHVsZTogJyArCiAgICAgICAgICAgICAgICAgICAgICAgIGFyZ3NbYXJncy5sZW5ndGggLSAxXSkpOwogICAgICAgICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgICAgICAgICAvL2FyZ3MgYXJlIGlkLCBkZXBzLCBmYWN0b3J5LiBTaG91bGQgYmUgbm9ybWFsaXplZCBieSB0aGUKICAgICAgICAgICAgICAgICAgICAvL2RlZmluZSgpIGZ1bmN0aW9uLgogICAgICAgICAgICAgICAgICAgIGNhbGxHZXRNb2R1bGUoYXJncyk7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICAgICAgY29udGV4dC5kZWZRdWV1ZU1hcCA9IHt9OwogICAgICAgIH0KCiAgICAgICAgY29udGV4dCA9IHsKICAgICAgICAgICAgY29uZmlnOiBjb25maWcsCiAgICAgICAgICAgIGNvbnRleHROYW1lOiBjb250ZXh0TmFtZSwKICAgICAgICAgICAgcmVnaXN0cnk6IHJlZ2lzdHJ5LAogICAgICAgICAgICBkZWZpbmVkOiBkZWZpbmVkLAogICAgICAgICAgICB1cmxGZXRjaGVkOiB1cmxGZXRjaGVkLAogICAgICAgICAgICBkZWZRdWV1ZTogZGVmUXVldWUsCiAgICAgICAgICAgIGRlZlF1ZXVlTWFwOiB7fSwKICAgICAgICAgICAgTW9kdWxlOiBNb2R1bGUsCiAgICAgICAgICAgIG1ha2VNb2R1bGVNYXA6IG1ha2VNb2R1bGVNYXAsCiAgICAgICAgICAgIG5leHRUaWNrOiByZXEubmV4dFRpY2ssCiAgICAgICAgICAgIG9uRXJyb3I6IG9uRXJyb3IsCgogICAgICAgICAgICAvKioKICAgICAgICAgICAgICogU2V0IGEgY29uZmlndXJhdGlvbiBmb3IgdGhlIGNvbnRleHQuCiAgICAgICAgICAgICAqIEBwYXJhbSB7T2JqZWN0fSBjZmcgY29uZmlnIG9iamVjdCB0byBpbnRlZ3JhdGUuCiAgICAgICAgICAgICAqLwogICAgICAgICAgICBjb25maWd1cmU6IGZ1bmN0aW9uIChjZmcpIHsKICAgICAgICAgICAgICAgIC8vTWFrZSBzdXJlIHRoZSBiYXNlVXJsIGVuZHMgaW4gYSBzbGFzaC4KICAgICAgICAgICAgICAgIGlmIChjZmcuYmFzZVVybCkgewogICAgICAgICAgICAgICAgICAgIGlmIChjZmcuYmFzZVVybC5jaGFyQXQoY2ZnLmJhc2VVcmwubGVuZ3RoIC0gMSkgIT09ICcvJykgewogICAgICAgICAgICAgICAgICAgICAgICBjZmcuYmFzZVVybCArPSAnLyc7CiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgIC8vU2F2ZSBvZmYgdGhlIHBhdGhzIHNpbmNlIHRoZXkgcmVxdWlyZSBzcGVjaWFsIHByb2Nlc3NpbmcsCiAgICAgICAgICAgICAgICAvL3RoZXkgYXJlIGFkZGl0aXZlLgogICAgICAgICAgICAgICAgdmFyIHNoaW0gPSBjb25maWcuc2hpbSwKICAgICAgICAgICAgICAgICAgICBvYmpzID0gewogICAgICAgICAgICAgICAgICAgICAgICBwYXRoczogdHJ1ZSwKICAgICAgICAgICAgICAgICAgICAgICAgYnVuZGxlczogdHJ1ZSwKICAgICAgICAgICAgICAgICAgICAgICAgY29uZmlnOiB0cnVlLAogICAgICAgICAgICAgICAgICAgICAgICBtYXA6IHRydWUKICAgICAgICAgICAgICAgICAgICB9OwoKICAgICAgICAgICAgICAgIGVhY2hQcm9wKGNmZywgZnVuY3Rpb24gKHZhbHVlLCBwcm9wKSB7CiAgICAgICAgICAgICAgICAgICAgaWYgKG9ianNbcHJvcF0pIHsKICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCFjb25maWdbcHJvcF0pIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbmZpZ1twcm9wXSA9IHt9OwogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIG1peGluKGNvbmZpZ1twcm9wXSwgdmFsdWUsIHRydWUsIHRydWUpOwogICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgICAgICAgICAgICAgIGNvbmZpZ1twcm9wXSA9IHZhbHVlOwogICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIH0pOwoKICAgICAgICAgICAgICAgIC8vUmV2ZXJzZSBtYXAgdGhlIGJ1bmRsZXMKICAgICAgICAgICAgICAgIGlmIChjZmcuYnVuZGxlcykgewogICAgICAgICAgICAgICAgICAgIGVhY2hQcm9wKGNmZy5idW5kbGVzLCBmdW5jdGlvbiAodmFsdWUsIHByb3ApIHsKICAgICAgICAgICAgICAgICAgICAgICAgZWFjaCh2YWx1ZSwgZnVuY3Rpb24gKHYpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmICh2ICE9PSBwcm9wKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnVuZGxlc01hcFt2XSA9IHByb3A7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0pOwogICAgICAgICAgICAgICAgICAgIH0pOwogICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgIC8vTWVyZ2Ugc2hpbQogICAgICAgICAgICAgICAgaWYgKGNmZy5zaGltKSB7CiAgICAgICAgICAgICAgICAgICAgZWFjaFByb3AoY2ZnLnNoaW0sIGZ1bmN0aW9uICh2YWx1ZSwgaWQpIHsKICAgICAgICAgICAgICAgICAgICAgICAgLy9Ob3JtYWxpemUgdGhlIHN0cnVjdHVyZQogICAgICAgICAgICAgICAgICAgICAgICBpZiAoaXNBcnJheSh2YWx1ZSkpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlID0gewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlcHM6IHZhbHVlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9OwogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIGlmICgodmFsdWUuZXhwb3J0cyB8fCB2YWx1ZS5pbml0KSAmJiAhdmFsdWUuZXhwb3J0c0ZuKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZS5leHBvcnRzRm4gPSBjb250ZXh0Lm1ha2VTaGltRXhwb3J0cyh2YWx1ZSk7CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgc2hpbVtpZF0gPSB2YWx1ZTsKICAgICAgICAgICAgICAgICAgICB9KTsKICAgICAgICAgICAgICAgICAgICBjb25maWcuc2hpbSA9IHNoaW07CiAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgLy9BZGp1c3QgcGFja2FnZXMgaWYgbmVjZXNzYXJ5LgogICAgICAgICAgICAgICAgaWYgKGNmZy5wYWNrYWdlcykgewogICAgICAgICAgICAgICAgICAgIGVhY2goY2ZnLnBhY2thZ2VzLCBmdW5jdGlvbiAocGtnT2JqKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIHZhciBsb2NhdGlvbiwgbmFtZTsKCiAgICAgICAgICAgICAgICAgICAgICAgIHBrZ09iaiA9IHR5cGVvZiBwa2dPYmogPT09ICdzdHJpbmcnID8ge25hbWU6IHBrZ09ian0gOiBwa2dPYmo7CgogICAgICAgICAgICAgICAgICAgICAgICBuYW1lID0gcGtnT2JqLm5hbWU7CiAgICAgICAgICAgICAgICAgICAgICAgIGxvY2F0aW9uID0gcGtnT2JqLmxvY2F0aW9uOwogICAgICAgICAgICAgICAgICAgICAgICBpZiAobG9jYXRpb24pIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbmZpZy5wYXRoc1tuYW1lXSA9IHBrZ09iai5sb2NhdGlvbjsKICAgICAgICAgICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgICAgICAgICAgLy9TYXZlIHBvaW50ZXIgdG8gbWFpbiBtb2R1bGUgSUQgZm9yIHBrZyBuYW1lLgogICAgICAgICAgICAgICAgICAgICAgICAvL1JlbW92ZSBsZWFkaW5nIGRvdCBpbiBtYWluLCBzbyBtYWluIHBhdGhzIGFyZSBub3JtYWxpemVkLAogICAgICAgICAgICAgICAgICAgICAgICAvL2FuZCByZW1vdmUgYW55IHRyYWlsaW5nIC5qcywgc2luY2UgZGlmZmVyZW50IHBhY2thZ2UKICAgICAgICAgICAgICAgICAgICAgICAgLy9lbnZzIGhhdmUgZGlmZmVyZW50IGNvbnZlbnRpb25zOiBzb21lIHVzZSBhIG1vZHVsZSBuYW1lLAogICAgICAgICAgICAgICAgICAgICAgICAvL3NvbWUgdXNlIGEgZmlsZSBuYW1lLgogICAgICAgICAgICAgICAgICAgICAgICBjb25maWcucGtnc1tuYW1lXSA9IHBrZ09iai5uYW1lICsgJy8nICsgKHBrZ09iai5tYWluIHx8ICdtYWluJykKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC5yZXBsYWNlKGN1cnJEaXJSZWdFeHAsICcnKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLnJlcGxhY2UoanNTdWZmaXhSZWdFeHAsICcnKTsKICAgICAgICAgICAgICAgICAgICB9KTsKICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICAvL0lmIHRoZXJlIGFyZSBhbnkgIndhaXRpbmcgdG8gZXhlY3V0ZSIgbW9kdWxlcyBpbiB0aGUgcmVnaXN0cnksCiAgICAgICAgICAgICAgICAvL3VwZGF0ZSB0aGUgbWFwcyBmb3IgdGhlbSwgc2luY2UgdGhlaXIgaW5mbywgbGlrZSBVUkxzIHRvIGxvYWQsCiAgICAgICAgICAgICAgICAvL21heSBoYXZlIGNoYW5nZWQuCiAgICAgICAgICAgICAgICBlYWNoUHJvcChyZWdpc3RyeSwgZnVuY3Rpb24gKG1vZCwgaWQpIHsKICAgICAgICAgICAgICAgICAgICAvL0lmIG1vZHVsZSBhbHJlYWR5IGhhcyBpbml0IGNhbGxlZCwgc2luY2UgaXQgaXMgdG9vCiAgICAgICAgICAgICAgICAgICAgLy9sYXRlIHRvIG1vZGlmeSB0aGVtLCBhbmQgaWdub3JlIHVubm9ybWFsaXplZCBvbmVzCiAgICAgICAgICAgICAgICAgICAgLy9zaW5jZSB0aGV5IGFyZSB0cmFuc2llbnQuCiAgICAgICAgICAgICAgICAgICAgaWYgKCFtb2QuaW5pdGVkICYmICFtb2QubWFwLnVubm9ybWFsaXplZCkgewogICAgICAgICAgICAgICAgICAgICAgICBtb2QubWFwID0gbWFrZU1vZHVsZU1hcChpZCwgbnVsbCwgdHJ1ZSk7CiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfSk7CgogICAgICAgICAgICAgICAgLy9JZiBhIGRlcHMgYXJyYXkgb3IgYSBjb25maWcgY2FsbGJhY2sgaXMgc3BlY2lmaWVkLCB0aGVuIGNhbGwKICAgICAgICAgICAgICAgIC8vcmVxdWlyZSB3aXRoIHRob3NlIGFyZ3MuIFRoaXMgaXMgdXNlZnVsIHdoZW4gcmVxdWlyZSBpcyBkZWZpbmVkIGFzIGEKICAgICAgICAgICAgICAgIC8vY29uZmlnIG9iamVjdCBiZWZvcmUgcmVxdWlyZS5qcyBpcyBsb2FkZWQuCiAgICAgICAgICAgICAgICBpZiAoY2ZnLmRlcHMgfHwgY2ZnLmNhbGxiYWNrKSB7CiAgICAgICAgICAgICAgICAgICAgY29udGV4dC5yZXF1aXJlKGNmZy5kZXBzIHx8IFtdLCBjZmcuY2FsbGJhY2spOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAoKICAgICAgICAgICAgbWFrZVNoaW1FeHBvcnRzOiBmdW5jdGlvbiAodmFsdWUpIHsKICAgICAgICAgICAgICAgIGZ1bmN0aW9uIGZuKCkgewogICAgICAgICAgICAgICAgICAgIHZhciByZXQ7CiAgICAgICAgICAgICAgICAgICAgaWYgKHZhbHVlLmluaXQpIHsKICAgICAgICAgICAgICAgICAgICAgICAgcmV0ID0gdmFsdWUuaW5pdC5hcHBseShnbG9iYWwsIGFyZ3VtZW50cyk7CiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgIHJldHVybiByZXQgfHwgKHZhbHVlLmV4cG9ydHMgJiYgZ2V0R2xvYmFsKHZhbHVlLmV4cG9ydHMpKTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIHJldHVybiBmbjsKICAgICAgICAgICAgfSwKCiAgICAgICAgICAgIG1ha2VSZXF1aXJlOiBmdW5jdGlvbiAocmVsTWFwLCBvcHRpb25zKSB7CiAgICAgICAgICAgICAgICBvcHRpb25zID0gb3B0aW9ucyB8fCB7fTsKCiAgICAgICAgICAgICAgICBmdW5jdGlvbiBsb2NhbFJlcXVpcmUoZGVwcywgY2FsbGJhY2ssIGVycmJhY2spIHsKICAgICAgICAgICAgICAgICAgICB2YXIgaWQsIG1hcCwgcmVxdWlyZU1vZDsKCiAgICAgICAgICAgICAgICAgICAgaWYgKG9wdGlvbnMuZW5hYmxlQnVpbGRDYWxsYmFjayAmJiBjYWxsYmFjayAmJiBpc0Z1bmN0aW9uKGNhbGxiYWNrKSkgewogICAgICAgICAgICAgICAgICAgICAgICBjYWxsYmFjay5fX3JlcXVpcmVKc0J1aWxkID0gdHJ1ZTsKICAgICAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgICAgIGlmICh0eXBlb2YgZGVwcyA9PT0gJ3N0cmluZycpIHsKICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGlzRnVuY3Rpb24oY2FsbGJhY2spKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvL0ludmFsaWQgY2FsbAogICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIG9uRXJyb3IobWFrZUVycm9yKCdyZXF1aXJlYXJncycsICdJbnZhbGlkIHJlcXVpcmUgY2FsbCcpLCBlcnJiYWNrKTsKICAgICAgICAgICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgICAgICAgICAgLy9JZiByZXF1aXJlfGV4cG9ydHN8bW9kdWxlIGFyZSByZXF1ZXN0ZWQsIGdldCB0aGUKICAgICAgICAgICAgICAgICAgICAgICAgLy92YWx1ZSBmb3IgdGhlbSBmcm9tIHRoZSBzcGVjaWFsIGhhbmRsZXJzLiBDYXZlYXQ6CiAgICAgICAgICAgICAgICAgICAgICAgIC8vdGhpcyBvbmx5IHdvcmtzIHdoaWxlIG1vZHVsZSBpcyBiZWluZyBkZWZpbmVkLgogICAgICAgICAgICAgICAgICAgICAgICBpZiAocmVsTWFwICYmIGhhc1Byb3AoaGFuZGxlcnMsIGRlcHMpKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gaGFuZGxlcnNbZGVwc10ocmVnaXN0cnlbcmVsTWFwLmlkXSk7CiAgICAgICAgICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICAgICAgICAgIC8vU3luY2hyb25vdXMgYWNjZXNzIHRvIG9uZSBtb2R1bGUuIElmIHJlcXVpcmUuZ2V0IGlzCiAgICAgICAgICAgICAgICAgICAgICAgIC8vYXZhaWxhYmxlIChhcyBpbiB0aGUgTm9kZSBhZGFwdGVyKSwgcHJlZmVyIHRoYXQuCiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChyZXEuZ2V0KSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gcmVxLmdldChjb250ZXh0LCBkZXBzLCByZWxNYXAsIGxvY2FsUmVxdWlyZSk7CiAgICAgICAgICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICAgICAgICAgIC8vTm9ybWFsaXplIG1vZHVsZSBuYW1lLCBpZiBpdCBjb250YWlucyAuIG9yIC4uCiAgICAgICAgICAgICAgICAgICAgICAgIG1hcCA9IG1ha2VNb2R1bGVNYXAoZGVwcywgcmVsTWFwLCBmYWxzZSwgdHJ1ZSk7CiAgICAgICAgICAgICAgICAgICAgICAgIGlkID0gbWFwLmlkOwoKICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCFoYXNQcm9wKGRlZmluZWQsIGlkKSkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIG9uRXJyb3IobWFrZUVycm9yKCdub3Rsb2FkZWQnLCAnTW9kdWxlIG5hbWUgIicgKwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWQgKwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJyIgaGFzIG5vdCBiZWVuIGxvYWRlZCB5ZXQgZm9yIGNvbnRleHQ6ICcgKwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29udGV4dE5hbWUgKwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKHJlbE1hcCA/ICcnIDogJy4gVXNlIHJlcXVpcmUoW10pJykpKTsKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gZGVmaW5lZFtpZF07CiAgICAgICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgICAgICAvL0dyYWIgZGVmaW5lcyB3YWl0aW5nIGluIHRoZSBnbG9iYWwgcXVldWUuCiAgICAgICAgICAgICAgICAgICAgaW50YWtlRGVmaW5lcygpOwoKICAgICAgICAgICAgICAgICAgICAvL01hcmsgYWxsIHRoZSBkZXBlbmRlbmNpZXMgYXMgbmVlZGluZyB0byBiZSBsb2FkZWQuCiAgICAgICAgICAgICAgICAgICAgY29udGV4dC5uZXh0VGljayhmdW5jdGlvbiAoKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIC8vU29tZSBkZWZpbmVzIGNvdWxkIGhhdmUgYmVlbiBhZGRlZCBzaW5jZSB0aGUKICAgICAgICAgICAgICAgICAgICAgICAgLy9yZXF1aXJlIGNhbGwsIGNvbGxlY3QgdGhlbS4KICAgICAgICAgICAgICAgICAgICAgICAgaW50YWtlRGVmaW5lcygpOwoKICAgICAgICAgICAgICAgICAgICAgICAgcmVxdWlyZU1vZCA9IGdldE1vZHVsZShtYWtlTW9kdWxlTWFwKG51bGwsIHJlbE1hcCkpOwoKICAgICAgICAgICAgICAgICAgICAgICAgLy9TdG9yZSBpZiBtYXAgY29uZmlnIHNob3VsZCBiZSBhcHBsaWVkIHRvIHRoaXMgcmVxdWlyZQogICAgICAgICAgICAgICAgICAgICAgICAvL2NhbGwgZm9yIGRlcGVuZGVuY2llcy4KICAgICAgICAgICAgICAgICAgICAgICAgcmVxdWlyZU1vZC5za2lwTWFwID0gb3B0aW9ucy5za2lwTWFwOwoKICAgICAgICAgICAgICAgICAgICAgICAgcmVxdWlyZU1vZC5pbml0KGRlcHMsIGNhbGxiYWNrLCBlcnJiYWNrLCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbmFibGVkOiB0cnVlCiAgICAgICAgICAgICAgICAgICAgICAgIH0pOwoKICAgICAgICAgICAgICAgICAgICAgICAgY2hlY2tMb2FkZWQoKTsKICAgICAgICAgICAgICAgICAgICB9KTsKCiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGxvY2FsUmVxdWlyZTsKICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICBtaXhpbihsb2NhbFJlcXVpcmUsIHsKICAgICAgICAgICAgICAgICAgICBpc0Jyb3dzZXI6IGlzQnJvd3NlciwKCiAgICAgICAgICAgICAgICAgICAgLyoqCiAgICAgICAgICAgICAgICAgICAgICogQ29udmVydHMgYSBtb2R1bGUgbmFtZSArIC5leHRlbnNpb24gaW50byBhbiBVUkwgcGF0aC4KICAgICAgICAgICAgICAgICAgICAgKiAqUmVxdWlyZXMqIHRoZSB1c2Ugb2YgYSBtb2R1bGUgbmFtZS4gSXQgZG9lcyBub3Qgc3VwcG9ydCB1c2luZwogICAgICAgICAgICAgICAgICAgICAqIHBsYWluIFVSTHMgbGlrZSBuYW1lVG9VcmwuCiAgICAgICAgICAgICAgICAgICAgICovCiAgICAgICAgICAgICAgICAgICAgdG9Vcmw6IGZ1bmN0aW9uIChtb2R1bGVOYW1lUGx1c0V4dCkgewogICAgICAgICAgICAgICAgICAgICAgICB2YXIgZXh0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5kZXggPSBtb2R1bGVOYW1lUGx1c0V4dC5sYXN0SW5kZXhPZignLicpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VnbWVudCA9IG1vZHVsZU5hbWVQbHVzRXh0LnNwbGl0KCcvJylbMF0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpc1JlbGF0aXZlID0gc2VnbWVudCA9PT0gJy4nIHx8IHNlZ21lbnQgPT09ICcuLic7CgogICAgICAgICAgICAgICAgICAgICAgICAvL0hhdmUgYSBmaWxlIGV4dGVuc2lvbiBhbGlhcywgYW5kIGl0IGlzIG5vdCB0aGUKICAgICAgICAgICAgICAgICAgICAgICAgLy9kb3RzIGZyb20gYSByZWxhdGl2ZSBwYXRoLgogICAgICAgICAgICAgICAgICAgICAgICBpZiAoaW5kZXggIT09IC0xICYmICghaXNSZWxhdGl2ZSB8fCBpbmRleCA+IDEpKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBleHQgPSBtb2R1bGVOYW1lUGx1c0V4dC5zdWJzdHJpbmcoaW5kZXgsIG1vZHVsZU5hbWVQbHVzRXh0Lmxlbmd0aCk7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtb2R1bGVOYW1lUGx1c0V4dCA9IG1vZHVsZU5hbWVQbHVzRXh0LnN1YnN0cmluZygwLCBpbmRleCk7CiAgICAgICAgICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiBjb250ZXh0Lm5hbWVUb1VybChub3JtYWxpemUobW9kdWxlTmFtZVBsdXNFeHQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlbE1hcCAmJiByZWxNYXAuaWQsIHRydWUpLCBleHQsICB0cnVlKTsKICAgICAgICAgICAgICAgICAgICB9LAoKICAgICAgICAgICAgICAgICAgICBkZWZpbmVkOiBmdW5jdGlvbiAoaWQpIHsKICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGhhc1Byb3AoZGVmaW5lZCwgbWFrZU1vZHVsZU1hcChpZCwgcmVsTWFwLCBmYWxzZSwgdHJ1ZSkuaWQpOwogICAgICAgICAgICAgICAgICAgIH0sCgogICAgICAgICAgICAgICAgICAgIHNwZWNpZmllZDogZnVuY3Rpb24gKGlkKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIGlkID0gbWFrZU1vZHVsZU1hcChpZCwgcmVsTWFwLCBmYWxzZSwgdHJ1ZSkuaWQ7CiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiBoYXNQcm9wKGRlZmluZWQsIGlkKSB8fCBoYXNQcm9wKHJlZ2lzdHJ5LCBpZCk7CiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfSk7CgogICAgICAgICAgICAgICAgLy9Pbmx5IGFsbG93IHVuZGVmIG9uIHRvcCBsZXZlbCByZXF1aXJlIGNhbGxzCiAgICAgICAgICAgICAgICBpZiAoIXJlbE1hcCkgewogICAgICAgICAgICAgICAgICAgIGxvY2FsUmVxdWlyZS51bmRlZiA9IGZ1bmN0aW9uIChpZCkgewogICAgICAgICAgICAgICAgICAgICAgICAvL0JpbmQgYW55IHdhaXRpbmcgZGVmaW5lKCkgY2FsbHMgdG8gdGhpcyBjb250ZXh0LAogICAgICAgICAgICAgICAgICAgICAgICAvL2ZpeCBmb3IgIzQwOAogICAgICAgICAgICAgICAgICAgICAgICB0YWtlR2xvYmFsUXVldWUoKTsKCiAgICAgICAgICAgICAgICAgICAgICAgIHZhciBtYXAgPSBtYWtlTW9kdWxlTWFwKGlkLCByZWxNYXAsIHRydWUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9kID0gZ2V0T3duKHJlZ2lzdHJ5LCBpZCk7CgogICAgICAgICAgICAgICAgICAgICAgICBtb2QudW5kZWZlZCA9IHRydWU7CiAgICAgICAgICAgICAgICAgICAgICAgIHJlbW92ZVNjcmlwdChpZCk7CgogICAgICAgICAgICAgICAgICAgICAgICBkZWxldGUgZGVmaW5lZFtpZF07CiAgICAgICAgICAgICAgICAgICAgICAgIGRlbGV0ZSB1cmxGZXRjaGVkW21hcC51cmxdOwogICAgICAgICAgICAgICAgICAgICAgICBkZWxldGUgdW5kZWZFdmVudHNbaWRdOwoKICAgICAgICAgICAgICAgICAgICAgICAgLy9DbGVhbiBxdWV1ZWQgZGVmaW5lcyB0b28uIEdvIGJhY2t3YXJkcwogICAgICAgICAgICAgICAgICAgICAgICAvL2luIGFycmF5IHNvIHRoYXQgdGhlIHNwbGljZXMgZG8gbm90CiAgICAgICAgICAgICAgICAgICAgICAgIC8vbWVzcyB1cCB0aGUgaXRlcmF0aW9uLgogICAgICAgICAgICAgICAgICAgICAgICBlYWNoUmV2ZXJzZShkZWZRdWV1ZSwgZnVuY3Rpb24oYXJncywgaSkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGFyZ3NbMF0gPT09IGlkKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVmUXVldWUuc3BsaWNlKGksIDEpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9KTsKICAgICAgICAgICAgICAgICAgICAgICAgZGVsZXRlIGNvbnRleHQuZGVmUXVldWVNYXBbaWRdOwoKICAgICAgICAgICAgICAgICAgICAgICAgaWYgKG1vZCkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgLy9Ib2xkIG9uIHRvIGxpc3RlbmVycyBpbiBjYXNlIHRoZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgLy9tb2R1bGUgd2lsbCBiZSBhdHRlbXB0ZWQgdG8gYmUgcmVsb2FkZWQKICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vdXNpbmcgYSBkaWZmZXJlbnQgY29uZmlnLgogICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKG1vZC5ldmVudHMuZGVmaW5lZCkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVuZGVmRXZlbnRzW2lkXSA9IG1vZC5ldmVudHM7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgICAgICAgICAgICAgY2xlYW5SZWdpc3RyeShpZCk7CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICB9OwogICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgIHJldHVybiBsb2NhbFJlcXVpcmU7CiAgICAgICAgICAgIH0sCgogICAgICAgICAgICAvKioKICAgICAgICAgICAgICogQ2FsbGVkIHRvIGVuYWJsZSBhIG1vZHVsZSBpZiBpdCBpcyBzdGlsbCBpbiB0aGUgcmVnaXN0cnkKICAgICAgICAgICAgICogYXdhaXRpbmcgZW5hYmxlbWVudC4gQSBzZWNvbmQgYXJnLCBwYXJlbnQsIHRoZSBwYXJlbnQgbW9kdWxlLAogICAgICAgICAgICAgKiBpcyBwYXNzZWQgaW4gZm9yIGNvbnRleHQsIHdoZW4gdGhpcyBtZXRob2QgaXMgb3ZlcnJpZGRlbiBieQogICAgICAgICAgICAgKiB0aGUgb3B0aW1pemVyLiBOb3Qgc2hvd24gaGVyZSB0byBrZWVwIGNvZGUgY29tcGFjdC4KICAgICAgICAgICAgICovCiAgICAgICAgICAgIGVuYWJsZTogZnVuY3Rpb24gKGRlcE1hcCkgewogICAgICAgICAgICAgICAgdmFyIG1vZCA9IGdldE93bihyZWdpc3RyeSwgZGVwTWFwLmlkKTsKICAgICAgICAgICAgICAgIGlmIChtb2QpIHsKICAgICAgICAgICAgICAgICAgICBnZXRNb2R1bGUoZGVwTWFwKS5lbmFibGUoKTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKCiAgICAgICAgICAgIC8qKgogICAgICAgICAgICAgKiBJbnRlcm5hbCBtZXRob2QgdXNlZCBieSBlbnZpcm9ubWVudCBhZGFwdGVycyB0byBjb21wbGV0ZSBhIGxvYWQgZXZlbnQuCiAgICAgICAgICAgICAqIEEgbG9hZCBldmVudCBjb3VsZCBiZSBhIHNjcmlwdCBsb2FkIG9yIGp1c3QgYSBsb2FkIHBhc3MgZnJvbSBhIHN5bmNocm9ub3VzCiAgICAgICAgICAgICAqIGxvYWQgY2FsbC4KICAgICAgICAgICAgICogQHBhcmFtIHtTdHJpbmd9IG1vZHVsZU5hbWUgdGhlIG5hbWUgb2YgdGhlIG1vZHVsZSB0byBwb3RlbnRpYWxseSBjb21wbGV0ZS4KICAgICAgICAgICAgICovCiAgICAgICAgICAgIGNvbXBsZXRlTG9hZDogZnVuY3Rpb24gKG1vZHVsZU5hbWUpIHsKICAgICAgICAgICAgICAgIHZhciBmb3VuZCwgYXJncywgbW9kLAogICAgICAgICAgICAgICAgICAgIHNoaW0gPSBnZXRPd24oY29uZmlnLnNoaW0sIG1vZHVsZU5hbWUpIHx8IHt9LAogICAgICAgICAgICAgICAgICAgIHNoRXhwb3J0cyA9IHNoaW0uZXhwb3J0czsKCiAgICAgICAgICAgICAgICB0YWtlR2xvYmFsUXVldWUoKTsKCiAgICAgICAgICAgICAgICB3aGlsZSAoZGVmUXVldWUubGVuZ3RoKSB7CiAgICAgICAgICAgICAgICAgICAgYXJncyA9IGRlZlF1ZXVlLnNoaWZ0KCk7CiAgICAgICAgICAgICAgICAgICAgaWYgKGFyZ3NbMF0gPT09IG51bGwpIHsKICAgICAgICAgICAgICAgICAgICAgICAgYXJnc1swXSA9IG1vZHVsZU5hbWU7CiAgICAgICAgICAgICAgICAgICAgICAgIC8vSWYgYWxyZWFkeSBmb3VuZCBhbiBhbm9ueW1vdXMgbW9kdWxlIGFuZCBib3VuZCBpdAogICAgICAgICAgICAgICAgICAgICAgICAvL3RvIHRoaXMgbmFtZSwgdGhlbiB0aGlzIGlzIHNvbWUgb3RoZXIgYW5vbiBtb2R1bGUKICAgICAgICAgICAgICAgICAgICAgICAgLy93YWl0aW5nIGZvciBpdHMgY29tcGxldGVMb2FkIHRvIGZpcmUuCiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChmb3VuZCkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgZm91bmQgPSB0cnVlOwogICAgICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAoYXJnc1swXSA9PT0gbW9kdWxlTmFtZSkgewogICAgICAgICAgICAgICAgICAgICAgICAvL0ZvdW5kIG1hdGNoaW5nIGRlZmluZSBjYWxsIGZvciB0aGlzIHNjcmlwdCEKICAgICAgICAgICAgICAgICAgICAgICAgZm91bmQgPSB0cnVlOwogICAgICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICAgICAgY2FsbEdldE1vZHVsZShhcmdzKTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIGNvbnRleHQuZGVmUXVldWVNYXAgPSB7fTsKCiAgICAgICAgICAgICAgICAvL0RvIHRoaXMgYWZ0ZXIgdGhlIGN5Y2xlIG9mIGNhbGxHZXRNb2R1bGUgaW4gY2FzZSB0aGUgcmVzdWx0CiAgICAgICAgICAgICAgICAvL29mIHRob3NlIGNhbGxzL2luaXQgY2FsbHMgY2hhbmdlcyB0aGUgcmVnaXN0cnkuCiAgICAgICAgICAgICAgICBtb2QgPSBnZXRPd24ocmVnaXN0cnksIG1vZHVsZU5hbWUpOwoKICAgICAgICAgICAgICAgIGlmICghZm91bmQgJiYgIWhhc1Byb3AoZGVmaW5lZCwgbW9kdWxlTmFtZSkgJiYgbW9kICYmICFtb2QuaW5pdGVkKSB7CiAgICAgICAgICAgICAgICAgICAgaWYgKGNvbmZpZy5lbmZvcmNlRGVmaW5lICYmICghc2hFeHBvcnRzIHx8ICFnZXRHbG9iYWwoc2hFeHBvcnRzKSkpIHsKICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGhhc1BhdGhGYWxsYmFjayhtb2R1bGVOYW1lKSkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuOwogICAgICAgICAgICAgICAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIG9uRXJyb3IobWFrZUVycm9yKCdub2RlZmluZScsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdObyBkZWZpbmUgY2FsbCBmb3IgJyArIG1vZHVsZU5hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG51bGwsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFttb2R1bGVOYW1lXSkpOwogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgICAgICAgICAgICAgLy9BIHNjcmlwdCB0aGF0IGRvZXMgbm90IGNhbGwgZGVmaW5lKCksIHNvIGp1c3Qgc2ltdWxhdGUKICAgICAgICAgICAgICAgICAgICAgICAgLy90aGUgY2FsbCBmb3IgaXQuCiAgICAgICAgICAgICAgICAgICAgICAgIGNhbGxHZXRNb2R1bGUoW21vZHVsZU5hbWUsIChzaGltLmRlcHMgfHwgW10pLCBzaGltLmV4cG9ydHNGbl0pOwogICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICBjaGVja0xvYWRlZCgpOwogICAgICAgICAgICB9LAoKICAgICAgICAgICAgLyoqCiAgICAgICAgICAgICAqIENvbnZlcnRzIGEgbW9kdWxlIG5hbWUgdG8gYSBmaWxlIHBhdGguIFN1cHBvcnRzIGNhc2VzIHdoZXJlCiAgICAgICAgICAgICAqIG1vZHVsZU5hbWUgbWF5IGFjdHVhbGx5IGJlIGp1c3QgYW4gVVJMLgogICAgICAgICAgICAgKiBOb3RlIHRoYXQgaXQgKipkb2VzIG5vdCoqIGNhbGwgbm9ybWFsaXplIG9uIHRoZSBtb2R1bGVOYW1lLAogICAgICAgICAgICAgKiBpdCBpcyBhc3N1bWVkIHRvIGhhdmUgYWxyZWFkeSBiZWVuIG5vcm1hbGl6ZWQuIFRoaXMgaXMgYW4KICAgICAgICAgICAgICogaW50ZXJuYWwgQVBJLCBub3QgYSBwdWJsaWMgb25lLiBVc2UgdG9VcmwgZm9yIHRoZSBwdWJsaWMgQVBJLgogICAgICAgICAgICAgKi8KICAgICAgICAgICAgbmFtZVRvVXJsOiBmdW5jdGlvbiAobW9kdWxlTmFtZSwgZXh0LCBza2lwRXh0KSB7CiAgICAgICAgICAgICAgICB2YXIgcGF0aHMsIHN5bXMsIGksIHBhcmVudE1vZHVsZSwgdXJsLAogICAgICAgICAgICAgICAgICAgIHBhcmVudFBhdGgsIGJ1bmRsZUlkLAogICAgICAgICAgICAgICAgICAgIHBrZ01haW4gPSBnZXRPd24oY29uZmlnLnBrZ3MsIG1vZHVsZU5hbWUpOwoKICAgICAgICAgICAgICAgIGlmIChwa2dNYWluKSB7CiAgICAgICAgICAgICAgICAgICAgbW9kdWxlTmFtZSA9IHBrZ01haW47CiAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgYnVuZGxlSWQgPSBnZXRPd24oYnVuZGxlc01hcCwgbW9kdWxlTmFtZSk7CgogICAgICAgICAgICAgICAgaWYgKGJ1bmRsZUlkKSB7CiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGNvbnRleHQubmFtZVRvVXJsKGJ1bmRsZUlkLCBleHQsIHNraXBFeHQpOwogICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgIC8vSWYgYSBjb2xvbiBpcyBpbiB0aGUgVVJMLCBpdCBpbmRpY2F0ZXMgYSBwcm90b2NvbCBpcyB1c2VkIGFuZCBpdCBpcyBqdXN0CiAgICAgICAgICAgICAgICAvL2FuIFVSTCB0byBhIGZpbGUsIG9yIGlmIGl0IHN0YXJ0cyB3aXRoIGEgc2xhc2gsIGNvbnRhaW5zIGEgcXVlcnkgYXJnIChpLmUuID8pCiAgICAgICAgICAgICAgICAvL29yIGVuZHMgd2l0aCAuanMsIHRoZW4gYXNzdW1lIHRoZSB1c2VyIG1lYW50IHRvIHVzZSBhbiB1cmwgYW5kIG5vdCBhIG1vZHVsZSBpZC4KICAgICAgICAgICAgICAgIC8vVGhlIHNsYXNoIGlzIGltcG9ydGFudCBmb3IgcHJvdG9jb2wtbGVzcyBVUkxzIGFzIHdlbGwgYXMgZnVsbCBwYXRocy4KICAgICAgICAgICAgICAgIGlmIChyZXEuanNFeHRSZWdFeHAudGVzdChtb2R1bGVOYW1lKSkgewogICAgICAgICAgICAgICAgICAgIC8vSnVzdCBhIHBsYWluIHBhdGgsIG5vdCBtb2R1bGUgbmFtZSBsb29rdXAsIHNvIGp1c3QgcmV0dXJuIGl0LgogICAgICAgICAgICAgICAgICAgIC8vQWRkIGV4dGVuc2lvbiBpZiBpdCBpcyBpbmNsdWRlZC4gVGhpcyBpcyBhIGJpdCB3b25reSwgb25seSBub24tLmpzIHRoaW5ncyBwYXNzCiAgICAgICAgICAgICAgICAgICAgLy9hbiBleHRlbnNpb24sIHRoaXMgbWV0aG9kIHByb2JhYmx5IG5lZWRzIHRvIGJlIHJld29ya2VkLgogICAgICAgICAgICAgICAgICAgIHVybCA9IG1vZHVsZU5hbWUgKyAoZXh0IHx8ICcnKTsKICAgICAgICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgICAgICAgICAgLy9BIG1vZHVsZSB0aGF0IG5lZWRzIHRvIGJlIGNvbnZlcnRlZCB0byBhIHBhdGguCiAgICAgICAgICAgICAgICAgICAgcGF0aHMgPSBjb25maWcucGF0aHM7CgogICAgICAgICAgICAgICAgICAgIHN5bXMgPSBtb2R1bGVOYW1lLnNwbGl0KCcvJyk7CiAgICAgICAgICAgICAgICAgICAgLy9Gb3IgZWFjaCBtb2R1bGUgbmFtZSBzZWdtZW50LCBzZWUgaWYgdGhlcmUgaXMgYSBwYXRoCiAgICAgICAgICAgICAgICAgICAgLy9yZWdpc3RlcmVkIGZvciBpdC4gU3RhcnQgd2l0aCBtb3N0IHNwZWNpZmljIG5hbWUKICAgICAgICAgICAgICAgICAgICAvL2FuZCB3b3JrIHVwIGZyb20gaXQuCiAgICAgICAgICAgICAgICAgICAgZm9yIChpID0gc3ltcy5sZW5ndGg7IGkgPiAwOyBpIC09IDEpIHsKICAgICAgICAgICAgICAgICAgICAgICAgcGFyZW50TW9kdWxlID0gc3ltcy5zbGljZSgwLCBpKS5qb2luKCcvJyk7CgogICAgICAgICAgICAgICAgICAgICAgICBwYXJlbnRQYXRoID0gZ2V0T3duKHBhdGhzLCBwYXJlbnRNb2R1bGUpOwogICAgICAgICAgICAgICAgICAgICAgICBpZiAocGFyZW50UGF0aCkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgLy9JZiBhbiBhcnJheSwgaXQgbWVhbnMgdGhlcmUgYXJlIGEgZmV3IGNob2ljZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvL0Nob29zZSB0aGUgb25lIHRoYXQgaXMgZGVzaXJlZAogICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGlzQXJyYXkocGFyZW50UGF0aCkpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXJlbnRQYXRoID0gcGFyZW50UGF0aFswXTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN5bXMuc3BsaWNlKDAsIGksIHBhcmVudFBhdGgpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgICAgIC8vSm9pbiB0aGUgcGF0aCBwYXJ0cyB0b2dldGhlciwgdGhlbiBmaWd1cmUgb3V0IGlmIGJhc2VVcmwgaXMgbmVlZGVkLgogICAgICAgICAgICAgICAgICAgIHVybCA9IHN5bXMuam9pbignLycpOwogICAgICAgICAgICAgICAgICAgIHVybCArPSAoZXh0IHx8ICgvXmRhdGFcOnxcPy8udGVzdCh1cmwpIHx8IHNraXBFeHQgPyAnJyA6ICcuanMnKSk7CiAgICAgICAgICAgICAgICAgICAgdXJsID0gKHVybC5jaGFyQXQoMCkgPT09ICcvJyB8fCB1cmwubWF0Y2goL15bXHdcK1wuXC1dKzovKSA/ICcnIDogY29uZmlnLmJhc2VVcmwpICsgdXJsOwogICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgIHJldHVybiBjb25maWcudXJsQXJncyA/IHVybCArCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoKHVybC5pbmRleE9mKCc/JykgPT09IC0xID8gJz8nIDogJyYnKSArCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29uZmlnLnVybEFyZ3MpIDogdXJsOwogICAgICAgICAgICB9LAoKICAgICAgICAgICAgLy9EZWxlZ2F0ZXMgdG8gcmVxLmxvYWQuIEJyb2tlbiBvdXQgYXMgYSBzZXBhcmF0ZSBmdW5jdGlvbiB0bwogICAgICAgICAgICAvL2FsbG93IG92ZXJyaWRpbmcgaW4gdGhlIG9wdGltaXplci4KICAgICAgICAgICAgbG9hZDogZnVuY3Rpb24gKGlkLCB1cmwpIHsKICAgICAgICAgICAgICAgIHJlcS5sb2FkKGNvbnRleHQsIGlkLCB1cmwpOwogICAgICAgICAgICB9LAoKICAgICAgICAgICAgLyoqCiAgICAgICAgICAgICAqIEV4ZWN1dGVzIGEgbW9kdWxlIGNhbGxiYWNrIGZ1bmN0aW9uLiBCcm9rZW4gb3V0IGFzIGEgc2VwYXJhdGUgZnVuY3Rpb24KICAgICAgICAgICAgICogc29sZWx5IHRvIGFsbG93IHRoZSBidWlsZCBzeXN0ZW0gdG8gc2VxdWVuY2UgdGhlIGZpbGVzIGluIHRoZSBidWlsdAogICAgICAgICAgICAgKiBsYXllciBpbiB0aGUgcmlnaHQgc2VxdWVuY2UuCiAgICAgICAgICAgICAqCiAgICAgICAgICAgICAqIEBwcml2YXRlCiAgICAgICAgICAgICAqLwogICAgICAgICAgICBleGVjQ2I6IGZ1bmN0aW9uIChuYW1lLCBjYWxsYmFjaywgYXJncywgZXhwb3J0cykgewogICAgICAgICAgICAgICAgcmV0dXJuIGNhbGxiYWNrLmFwcGx5KGV4cG9ydHMsIGFyZ3MpOwogICAgICAgICAgICB9LAoKICAgICAgICAgICAgLyoqCiAgICAgICAgICAgICAqIGNhbGxiYWNrIGZvciBzY3JpcHQgbG9hZHMsIHVzZWQgdG8gY2hlY2sgc3RhdHVzIG9mIGxvYWRpbmcuCiAgICAgICAgICAgICAqCiAgICAgICAgICAgICAqIEBwYXJhbSB7RXZlbnR9IGV2dCB0aGUgZXZlbnQgZnJvbSB0aGUgYnJvd3NlciBmb3IgdGhlIHNjcmlwdAogICAgICAgICAgICAgKiB0aGF0IHdhcyBsb2FkZWQuCiAgICAgICAgICAgICAqLwogICAgICAgICAgICBvblNjcmlwdExvYWQ6IGZ1bmN0aW9uIChldnQpIHsKICAgICAgICAgICAgICAgIC8vVXNpbmcgY3VycmVudFRhcmdldCBpbnN0ZWFkIG9mIHRhcmdldCBmb3IgRmlyZWZveCAyLjAncyBzYWtlLiBOb3QKICAgICAgICAgICAgICAgIC8vYWxsIG9sZCBicm93c2VycyB3aWxsIGJlIHN1cHBvcnRlZCwgYnV0IHRoaXMgb25lIHdhcyBlYXN5IGVub3VnaAogICAgICAgICAgICAgICAgLy90byBzdXBwb3J0IGFuZCBzdGlsbCBtYWtlcyBzZW5zZS4KICAgICAgICAgICAgICAgIGlmIChldnQudHlwZSA9PT0gJ2xvYWQnIHx8CiAgICAgICAgICAgICAgICAgICAgICAgIChyZWFkeVJlZ0V4cC50ZXN0KChldnQuY3VycmVudFRhcmdldCB8fCBldnQuc3JjRWxlbWVudCkucmVhZHlTdGF0ZSkpKSB7CiAgICAgICAgICAgICAgICAgICAgLy9SZXNldCBpbnRlcmFjdGl2ZSBzY3JpcHQgc28gYSBzY3JpcHQgbm9kZSBpcyBub3QgaGVsZCBvbnRvIGZvcgogICAgICAgICAgICAgICAgICAgIC8vdG8gbG9uZy4KICAgICAgICAgICAgICAgICAgICBpbnRlcmFjdGl2ZVNjcmlwdCA9IG51bGw7CgogICAgICAgICAgICAgICAgICAgIC8vUHVsbCBvdXQgdGhlIG5hbWUgb2YgdGhlIG1vZHVsZSBhbmQgdGhlIGNvbnRleHQuCiAgICAgICAgICAgICAgICAgICAgdmFyIGRhdGEgPSBnZXRTY3JpcHREYXRhKGV2dCk7CiAgICAgICAgICAgICAgICAgICAgY29udGV4dC5jb21wbGV0ZUxvYWQoZGF0YS5pZCk7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCgogICAgICAgICAgICAvKioKICAgICAgICAgICAgICogQ2FsbGJhY2sgZm9yIHNjcmlwdCBlcnJvcnMuCiAgICAgICAgICAgICAqLwogICAgICAgICAgICBvblNjcmlwdEVycm9yOiBmdW5jdGlvbiAoZXZ0KSB7CiAgICAgICAgICAgICAgICB2YXIgZGF0YSA9IGdldFNjcmlwdERhdGEoZXZ0KTsKICAgICAgICAgICAgICAgIGlmICghaGFzUGF0aEZhbGxiYWNrKGRhdGEuaWQpKSB7CiAgICAgICAgICAgICAgICAgICAgdmFyIHBhcmVudHMgPSBbXTsKICAgICAgICAgICAgICAgICAgICBlYWNoUHJvcChyZWdpc3RyeSwgZnVuY3Rpb24odmFsdWUsIGtleSkgewogICAgICAgICAgICAgICAgICAgICAgICBpZiAoa2V5LmluZGV4T2YoJ19AcicpICE9PSAwKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBlYWNoKHZhbHVlLmRlcE1hcHMsIGZ1bmN0aW9uKGRlcE1hcCkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIChkZXBNYXAuaWQgPT09IGRhdGEuaWQpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFyZW50cy5wdXNoKGtleSk7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiB0cnVlOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgfSk7CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICB9KTsKICAgICAgICAgICAgICAgICAgICByZXR1cm4gb25FcnJvcihtYWtlRXJyb3IoJ3NjcmlwdGVycm9yJywgJ1NjcmlwdCBlcnJvciBmb3IgIicgKyBkYXRhLmlkICsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKHBhcmVudHMubGVuZ3RoID8KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJyIsIG5lZWRlZCBieTogJyArIHBhcmVudHMuam9pbignLCAnKSA6CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICciJyksIGV2dCwgW2RhdGEuaWRdKSk7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICB9OwoKICAgICAgICBjb250ZXh0LnJlcXVpcmUgPSBjb250ZXh0Lm1ha2VSZXF1aXJlKCk7CiAgICAgICAgcmV0dXJuIGNvbnRleHQ7CiAgICB9CgogICAgLyoqCiAgICAgKiBNYWluIGVudHJ5IHBvaW50LgogICAgICoKICAgICAqIElmIHRoZSBvbmx5IGFyZ3VtZW50IHRvIHJlcXVpcmUgaXMgYSBzdHJpbmcsIHRoZW4gdGhlIG1vZHVsZSB0aGF0CiAgICAgKiBpcyByZXByZXNlbnRlZCBieSB0aGF0IHN0cmluZyBpcyBmZXRjaGVkIGZvciB0aGUgYXBwcm9wcmlhdGUgY29udGV4dC4KICAgICAqCiAgICAgKiBJZiB0aGUgZmlyc3QgYXJndW1lbnQgaXMgYW4gYXJyYXksIHRoZW4gaXQgd2lsbCBiZSB0cmVhdGVkIGFzIGFuIGFycmF5CiAgICAgKiBvZiBkZXBlbmRlbmN5IHN0cmluZyBuYW1lcyB0byBmZXRjaC4gQW4gb3B0aW9uYWwgZnVuY3Rpb24gY2FsbGJhY2sgY2FuCiAgICAgKiBiZSBzcGVjaWZpZWQgdG8gZXhlY3V0ZSB3aGVuIGFsbCBvZiB0aG9zZSBkZXBlbmRlbmNpZXMgYXJlIGF2YWlsYWJsZS4KICAgICAqCiAgICAgKiBNYWtlIGEgbG9jYWwgcmVxIHZhcmlhYmxlIHRvIGhlbHAgQ2FqYSBjb21wbGlhbmNlIChpdCBhc3N1bWVzIHRoaW5ncwogICAgICogb24gYSByZXF1aXJlIHRoYXQgYXJlIG5vdCBzdGFuZGFyZGl6ZWQpLCBhbmQgdG8gZ2l2ZSBhIHNob3J0CiAgICAgKiBuYW1lIGZvciBtaW5pZmljYXRpb24vbG9jYWwgc2NvcGUgdXNlLgogICAgICovCiAgICByZXEgPSByZXF1aXJlanMgPSBmdW5jdGlvbiAoZGVwcywgY2FsbGJhY2ssIGVycmJhY2ssIG9wdGlvbmFsKSB7CgogICAgICAgIC8vRmluZCB0aGUgcmlnaHQgY29udGV4dCwgdXNlIGRlZmF1bHQKICAgICAgICB2YXIgY29udGV4dCwgY29uZmlnLAogICAgICAgICAgICBjb250ZXh0TmFtZSA9IGRlZkNvbnRleHROYW1lOwoKICAgICAgICAvLyBEZXRlcm1pbmUgaWYgaGF2ZSBjb25maWcgb2JqZWN0IGluIHRoZSBjYWxsLgogICAgICAgIGlmICghaXNBcnJheShkZXBzKSAmJiB0eXBlb2YgZGVwcyAhPT0gJ3N0cmluZycpIHsKICAgICAgICAgICAgLy8gZGVwcyBpcyBhIGNvbmZpZyBvYmplY3QKICAgICAgICAgICAgY29uZmlnID0gZGVwczsKICAgICAgICAgICAgaWYgKGlzQXJyYXkoY2FsbGJhY2spKSB7CiAgICAgICAgICAgICAgICAvLyBBZGp1c3QgYXJncyBpZiB0aGVyZSBhcmUgZGVwZW5kZW5jaWVzCiAgICAgICAgICAgICAgICBkZXBzID0gY2FsbGJhY2s7CiAgICAgICAgICAgICAgICBjYWxsYmFjayA9IGVycmJhY2s7CiAgICAgICAgICAgICAgICBlcnJiYWNrID0gb3B0aW9uYWw7CiAgICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgICAgICBkZXBzID0gW107CiAgICAgICAgICAgIH0KICAgICAgICB9CgogICAgICAgIGlmIChjb25maWcgJiYgY29uZmlnLmNvbnRleHQpIHsKICAgICAgICAgICAgY29udGV4dE5hbWUgPSBjb25maWcuY29udGV4dDsKICAgICAgICB9CgogICAgICAgIGNvbnRleHQgPSBnZXRPd24oY29udGV4dHMsIGNvbnRleHROYW1lKTsKICAgICAgICBpZiAoIWNvbnRleHQpIHsKICAgICAgICAgICAgY29udGV4dCA9IGNvbnRleHRzW2NvbnRleHROYW1lXSA9IHJlcS5zLm5ld0NvbnRleHQoY29udGV4dE5hbWUpOwogICAgICAgIH0KCiAgICAgICAgaWYgKGNvbmZpZykgewogICAgICAgICAgICBjb250ZXh0LmNvbmZpZ3VyZShjb25maWcpOwogICAgICAgIH0KCiAgICAgICAgcmV0dXJuIGNvbnRleHQucmVxdWlyZShkZXBzLCBjYWxsYmFjaywgZXJyYmFjayk7CiAgICB9OwoKICAgIC8qKgogICAgICogU3VwcG9ydCByZXF1aXJlLmNvbmZpZygpIHRvIG1ha2UgaXQgZWFzaWVyIHRvIGNvb3BlcmF0ZSB3aXRoIG90aGVyCiAgICAgKiBBTUQgbG9hZGVycyBvbiBnbG9iYWxseSBhZ3JlZWQgbmFtZXMuCiAgICAgKi8KICAgIHJlcS5jb25maWcgPSBmdW5jdGlvbiAoY29uZmlnKSB7CiAgICAgICAgcmV0dXJuIHJlcShjb25maWcpOwogICAgfTsKCiAgICAvKioKICAgICAqIEV4ZWN1dGUgc29tZXRoaW5nIGFmdGVyIHRoZSBjdXJyZW50IHRpY2sKICAgICAqIG9mIHRoZSBldmVudCBsb29wLiBPdmVycmlkZSBmb3Igb3RoZXIgZW52cwogICAgICogdGhhdCBoYXZlIGEgYmV0dGVyIHNvbHV0aW9uIHRoYW4gc2V0VGltZW91dC4KICAgICAqIEBwYXJhbSAge0Z1bmN0aW9ufSBmbiBmdW5jdGlvbiB0byBleGVjdXRlIGxhdGVyLgogICAgICovCiAgICByZXEubmV4dFRpY2sgPSB0eXBlb2Ygc2V0VGltZW91dCAhPT0gJ3VuZGVmaW5lZCcgPyBmdW5jdGlvbiAoZm4pIHsKICAgICAgICBzZXRUaW1lb3V0KGZuLCA0KTsKICAgIH0gOiBmdW5jdGlvbiAoZm4pIHsgZm4oKTsgfTsKCiAgICAvKioKICAgICAqIEV4cG9ydCByZXF1aXJlIGFzIGEgZ2xvYmFsLCBidXQgb25seSBpZiBpdCBkb2VzIG5vdCBhbHJlYWR5IGV4aXN0LgogICAgICovCiAgICBpZiAoIXJlcXVpcmUpIHsKICAgICAgICByZXF1aXJlID0gcmVxOwogICAgfQoKICAgIHJlcS52ZXJzaW9uID0gdmVyc2lvbjsKCiAgICAvL1VzZWQgdG8gZmlsdGVyIG91dCBkZXBlbmRlbmNpZXMgdGhhdCBhcmUgYWxyZWFkeSBwYXRocy4KICAgIHJlcS5qc0V4dFJlZ0V4cCA9IC9eXC98OnxcP3xcLmpzJC87CiAgICByZXEuaXNCcm93c2VyID0gaXNCcm93c2VyOwogICAgcyA9IHJlcS5zID0gewogICAgICAgIGNvbnRleHRzOiBjb250ZXh0cywKICAgICAgICBuZXdDb250ZXh0OiBuZXdDb250ZXh0CiAgICB9OwoKICAgIC8vQ3JlYXRlIGRlZmF1bHQgY29udGV4dC4KICAgIHJlcSh7fSk7CgogICAgLy9FeHBvcnRzIHNvbWUgY29udGV4dC1zZW5zaXRpdmUgbWV0aG9kcyBvbiBnbG9iYWwgcmVxdWlyZS4KICAgIGVhY2goWwogICAgICAgICd0b1VybCcsCiAgICAgICAgJ3VuZGVmJywKICAgICAgICAnZGVmaW5lZCcsCiAgICAgICAgJ3NwZWNpZmllZCcKICAgIF0sIGZ1bmN0aW9uIChwcm9wKSB7CiAgICAgICAgLy9SZWZlcmVuY2UgZnJvbSBjb250ZXh0cyBpbnN0ZWFkIG9mIGVhcmx5IGJpbmRpbmcgdG8gZGVmYXVsdCBjb250ZXh0LAogICAgICAgIC8vc28gdGhhdCBkdXJpbmcgYnVpbGRzLCB0aGUgbGF0ZXN0IGluc3RhbmNlIG9mIHRoZSBkZWZhdWx0IGNvbnRleHQKICAgICAgICAvL3dpdGggaXRzIGNvbmZpZyBnZXRzIHVzZWQuCiAgICAgICAgcmVxW3Byb3BdID0gZnVuY3Rpb24gKCkgewogICAgICAgICAgICB2YXIgY3R4ID0gY29udGV4dHNbZGVmQ29udGV4dE5hbWVdOwogICAgICAgICAgICByZXR1cm4gY3R4LnJlcXVpcmVbcHJvcF0uYXBwbHkoY3R4LCBhcmd1bWVudHMpOwogICAgICAgIH07CiAgICB9KTsKCiAgICBpZiAoaXNCcm93c2VyKSB7CiAgICAgICAgaGVhZCA9IHMuaGVhZCA9IGRvY3VtZW50LmdldEVsZW1lbnRzQnlUYWdOYW1lKCdoZWFkJylbMF07CiAgICAgICAgLy9JZiBCQVNFIHRhZyBpcyBpbiBwbGF5LCB1c2luZyBhcHBlbmRDaGlsZCBpcyBhIHByb2JsZW0gZm9yIElFNi4KICAgICAgICAvL1doZW4gdGhhdCBicm93c2VyIGRpZXMsIHRoaXMgY2FuIGJlIHJlbW92ZWQuIERldGFpbHMgaW4gdGhpcyBqUXVlcnkgYnVnOgogICAgICAgIC8vaHR0cDovL2Rldi5qcXVlcnkuY29tL3RpY2tldC8yNzA5CiAgICAgICAgYmFzZUVsZW1lbnQgPSBkb2N1bWVudC5nZXRFbGVtZW50c0J5VGFnTmFtZSgnYmFzZScpWzBdOwogICAgICAgIGlmIChiYXNlRWxlbWVudCkgewogICAgICAgICAgICBoZWFkID0gcy5oZWFkID0gYmFzZUVsZW1lbnQucGFyZW50Tm9kZTsKICAgICAgICB9CiAgICB9CgogICAgLyoqCiAgICAgKiBBbnkgZXJyb3JzIHRoYXQgcmVxdWlyZSBleHBsaWNpdGx5IGdlbmVyYXRlcyB3aWxsIGJlIHBhc3NlZCB0byB0aGlzCiAgICAgKiBmdW5jdGlvbi4gSW50ZXJjZXB0L292ZXJyaWRlIGl0IGlmIHlvdSB3YW50IGN1c3RvbSBlcnJvciBoYW5kbGluZy4KICAgICAqIEBwYXJhbSB7RXJyb3J9IGVyciB0aGUgZXJyb3Igb2JqZWN0LgogICAgICovCiAgICByZXEub25FcnJvciA9IGRlZmF1bHRPbkVycm9yOwoKICAgIC8qKgogICAgICogQ3JlYXRlcyB0aGUgbm9kZSBmb3IgdGhlIGxvYWQgY29tbWFuZC4gT25seSB1c2VkIGluIGJyb3dzZXIgZW52cy4KICAgICAqLwogICAgcmVxLmNyZWF0ZU5vZGUgPSBmdW5jdGlvbiAoY29uZmlnLCBtb2R1bGVOYW1lLCB1cmwpIHsKICAgICAgICB2YXIgbm9kZSA9IGNvbmZpZy54aHRtbCA/CiAgICAgICAgICAgICAgICBkb2N1bWVudC5jcmVhdGVFbGVtZW50TlMoJ2h0dHA6Ly93d3cudzMub3JnLzE5OTkveGh0bWwnLCAnaHRtbDpzY3JpcHQnKSA6CiAgICAgICAgICAgICAgICBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzY3JpcHQnKTsKICAgICAgICBub2RlLnR5cGUgPSBjb25maWcuc2NyaXB0VHlwZSB8fCAndGV4dC9qYXZhc2NyaXB0JzsKICAgICAgICBub2RlLmNoYXJzZXQgPSAndXRmLTgnOwogICAgICAgIG5vZGUuYXN5bmMgPSB0cnVlOwogICAgICAgIHJldHVybiBub2RlOwogICAgfTsKCiAgICAvKioKICAgICAqIERvZXMgdGhlIHJlcXVlc3QgdG8gbG9hZCBhIG1vZHVsZSBmb3IgdGhlIGJyb3dzZXIgY2FzZS4KICAgICAqIE1ha2UgdGhpcyBhIHNlcGFyYXRlIGZ1bmN0aW9uIHRvIGFsbG93IG90aGVyIGVudmlyb25tZW50cwogICAgICogdG8gb3ZlcnJpZGUgaXQuCiAgICAgKgogICAgICogQHBhcmFtIHtPYmplY3R9IGNvbnRleHQgdGhlIHJlcXVpcmUgY29udGV4dCB0byBmaW5kIHN0YXRlLgogICAgICogQHBhcmFtIHtTdHJpbmd9IG1vZHVsZU5hbWUgdGhlIG5hbWUgb2YgdGhlIG1vZHVsZS4KICAgICAqIEBwYXJhbSB7T2JqZWN0fSB1cmwgdGhlIFVSTCB0byB0aGUgbW9kdWxlLgogICAgICovCiAgICByZXEubG9hZCA9IGZ1bmN0aW9uIChjb250ZXh0LCBtb2R1bGVOYW1lLCB1cmwpIHsKICAgICAgICB2YXIgY29uZmlnID0gKGNvbnRleHQgJiYgY29udGV4dC5jb25maWcpIHx8IHt9LAogICAgICAgICAgICBub2RlOwogICAgICAgIGlmIChpc0Jyb3dzZXIpIHsKICAgICAgICAgICAgLy9JbiB0aGUgYnJvd3NlciBzbyB1c2UgYSBzY3JpcHQgdGFnCiAgICAgICAgICAgIG5vZGUgPSByZXEuY3JlYXRlTm9kZShjb25maWcsIG1vZHVsZU5hbWUsIHVybCk7CiAgICAgICAgICAgIGlmIChjb25maWcub25Ob2RlQ3JlYXRlZCkgewogICAgICAgICAgICAgICAgY29uZmlnLm9uTm9kZUNyZWF0ZWQobm9kZSwgY29uZmlnLCBtb2R1bGVOYW1lLCB1cmwpOwogICAgICAgICAgICB9CgogICAgICAgICAgICBub2RlLnNldEF0dHJpYnV0ZSgnZGF0YS1yZXF1aXJlY29udGV4dCcsIGNvbnRleHQuY29udGV4dE5hbWUpOwogICAgICAgICAgICBub2RlLnNldEF0dHJpYnV0ZSgnZGF0YS1yZXF1aXJlbW9kdWxlJywgbW9kdWxlTmFtZSk7CgogICAgICAgICAgICAvL1NldCB1cCBsb2FkIGxpc3RlbmVyLiBUZXN0IGF0dGFjaEV2ZW50IGZpcnN0IGJlY2F1c2UgSUU5IGhhcwogICAgICAgICAgICAvL2Egc3VidGxlIGlzc3VlIGluIGl0cyBhZGRFdmVudExpc3RlbmVyIGFuZCBzY3JpcHQgb25sb2FkIGZpcmluZ3MKICAgICAgICAgICAgLy90aGF0IGRvIG5vdCBtYXRjaCB0aGUgYmVoYXZpb3Igb2YgYWxsIG90aGVyIGJyb3dzZXJzIHdpdGgKICAgICAgICAgICAgLy9hZGRFdmVudExpc3RlbmVyIHN1cHBvcnQsIHdoaWNoIGZpcmUgdGhlIG9ubG9hZCBldmVudCBmb3IgYQogICAgICAgICAgICAvL3NjcmlwdCByaWdodCBhZnRlciB0aGUgc2NyaXB0IGV4ZWN1dGlvbi4gU2VlOgogICAgICAgICAgICAvL2h0dHBzOi8vY29ubmVjdC5taWNyb3NvZnQuY29tL0lFL2ZlZWRiYWNrL2RldGFpbHMvNjQ4MDU3L3NjcmlwdC1vbmxvYWQtZXZlbnQtaXMtbm90LWZpcmVkLWltbWVkaWF0ZWx5LWFmdGVyLXNjcmlwdC1leGVjdXRpb24KICAgICAgICAgICAgLy9VTkZPUlRVTkFURUxZIE9wZXJhIGltcGxlbWVudHMgYXR0YWNoRXZlbnQgYnV0IGRvZXMgbm90IGZvbGxvdyB0aGUgc2NyaXB0CiAgICAgICAgICAgIC8vc2NyaXB0IGV4ZWN1dGlvbiBtb2RlLgogICAgICAgICAgICBpZiAobm9kZS5hdHRhY2hFdmVudCAmJgogICAgICAgICAgICAgICAgICAgIC8vQ2hlY2sgaWYgbm9kZS5hdHRhY2hFdmVudCBpcyBhcnRpZmljaWFsbHkgYWRkZWQgYnkgY3VzdG9tIHNjcmlwdCBvcgogICAgICAgICAgICAgICAgICAgIC8vbmF0aXZlbHkgc3VwcG9ydGVkIGJ5IGJyb3dzZXIKICAgICAgICAgICAgICAgICAgICAvL3JlYWQgaHR0cHM6Ly9naXRodWIuY29tL2pyYnVya2UvcmVxdWlyZWpzL2lzc3Vlcy8xODcKICAgICAgICAgICAgICAgICAgICAvL2lmIHdlIGNhbiBOT1QgZmluZCBbbmF0aXZlIGNvZGVdIHRoZW4gaXQgbXVzdCBOT1QgbmF0aXZlbHkgc3VwcG9ydGVkLgogICAgICAgICAgICAgICAgICAgIC8vaW4gSUU4LCBub2RlLmF0dGFjaEV2ZW50IGRvZXMgbm90IGhhdmUgdG9TdHJpbmcoKQogICAgICAgICAgICAgICAgICAgIC8vTm90ZSB0aGUgdGVzdCBmb3IgIltuYXRpdmUgY29kZSIgd2l0aCBubyBjbG9zaW5nIGJyYWNlLCBzZWU6CiAgICAgICAgICAgICAgICAgICAgLy9odHRwczovL2dpdGh1Yi5jb20vanJidXJrZS9yZXF1aXJlanMvaXNzdWVzLzI3MwogICAgICAgICAgICAgICAgICAgICEobm9kZS5hdHRhY2hFdmVudC50b1N0cmluZyAmJiBub2RlLmF0dGFjaEV2ZW50LnRvU3RyaW5nKCkuaW5kZXhPZignW25hdGl2ZSBjb2RlJykgPCAwKSAmJgogICAgICAgICAgICAgICAgICAgICFpc09wZXJhKSB7CiAgICAgICAgICAgICAgICAvL1Byb2JhYmx5IElFLiBJRSAoYXQgbGVhc3QgNi04KSBkbyBub3QgZmlyZQogICAgICAgICAgICAgICAgLy9zY3JpcHQgb25sb2FkIHJpZ2h0IGFmdGVyIGV4ZWN1dGluZyB0aGUgc2NyaXB0LCBzbwogICAgICAgICAgICAgICAgLy93ZSBjYW5ub3QgdGllIHRoZSBhbm9ueW1vdXMgZGVmaW5lIGNhbGwgdG8gYSBuYW1lLgogICAgICAgICAgICAgICAgLy9Ib3dldmVyLCBJRSByZXBvcnRzIHRoZSBzY3JpcHQgYXMgYmVpbmcgaW4gJ2ludGVyYWN0aXZlJwogICAgICAgICAgICAgICAgLy9yZWFkeVN0YXRlIGF0IHRoZSB0aW1lIG9mIHRoZSBkZWZpbmUgY2FsbC4KICAgICAgICAgICAgICAgIHVzZUludGVyYWN0aXZlID0gdHJ1ZTsKCiAgICAgICAgICAgICAgICBub2RlLmF0dGFjaEV2ZW50KCdvbnJlYWR5c3RhdGVjaGFuZ2UnLCBjb250ZXh0Lm9uU2NyaXB0TG9hZCk7CiAgICAgICAgICAgICAgICAvL0l0IHdvdWxkIGJlIGdyZWF0IHRvIGFkZCBhbiBlcnJvciBoYW5kbGVyIGhlcmUgdG8gY2F0Y2gKICAgICAgICAgICAgICAgIC8vNDA0cyBpbiBJRTkrLiBIb3dldmVyLCBvbnJlYWR5c3RhdGVjaGFuZ2Ugd2lsbCBmaXJlIGJlZm9yZQogICAgICAgICAgICAgICAgLy90aGUgZXJyb3IgaGFuZGxlciwgc28gdGhhdCBkb2VzIG5vdCBoZWxwLiBJZiBhZGRFdmVudExpc3RlbmVyCiAgICAgICAgICAgICAgICAvL2lzIHVzZWQsIHRoZW4gSUUgd2lsbCBmaXJlIGVycm9yIGJlZm9yZSBsb2FkLCBidXQgd2UgY2Fubm90CiAgICAgICAgICAgICAgICAvL3VzZSB0aGF0IHBhdGh3YXkgZ2l2ZW4gdGhlIGNvbm5lY3QubWljcm9zb2Z0LmNvbSBpc3N1ZQogICAgICAgICAgICAgICAgLy9tZW50aW9uZWQgYWJvdmUgYWJvdXQgbm90IGRvaW5nIHRoZSAnc2NyaXB0IGV4ZWN1dGUsCiAgICAgICAgICAgICAgICAvL3RoZW4gZmlyZSB0aGUgc2NyaXB0IGxvYWQgZXZlbnQgbGlzdGVuZXIgYmVmb3JlIGV4ZWN1dGUKICAgICAgICAgICAgICAgIC8vbmV4dCBzY3JpcHQnIHRoYXQgb3RoZXIgYnJvd3NlcnMgZG8uCiAgICAgICAgICAgICAgICAvL0Jlc3QgaG9wZTogSUUxMCBmaXhlcyB0aGUgaXNzdWVzLAogICAgICAgICAgICAgICAgLy9hbmQgdGhlbiBkZXN0cm95cyBhbGwgaW5zdGFsbHMgb2YgSUUgNi05LgogICAgICAgICAgICAgICAgLy9ub2RlLmF0dGFjaEV2ZW50KCdvbmVycm9yJywgY29udGV4dC5vblNjcmlwdEVycm9yKTsKICAgICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgICAgIG5vZGUuYWRkRXZlbnRMaXN0ZW5lcignbG9hZCcsIGNvbnRleHQub25TY3JpcHRMb2FkLCBmYWxzZSk7CiAgICAgICAgICAgICAgICBub2RlLmFkZEV2ZW50TGlzdGVuZXIoJ2Vycm9yJywgY29udGV4dC5vblNjcmlwdEVycm9yLCBmYWxzZSk7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgbm9kZS5zcmMgPSB1cmw7CgogICAgICAgICAgICAvL0ZvciBzb21lIGNhY2hlIGNhc2VzIGluIElFIDYtOCwgdGhlIHNjcmlwdCBleGVjdXRlcyBiZWZvcmUgdGhlIGVuZAogICAgICAgICAgICAvL29mIHRoZSBhcHBlbmRDaGlsZCBleGVjdXRpb24sIHNvIHRvIHRpZSBhbiBhbm9ueW1vdXMgZGVmaW5lCiAgICAgICAgICAgIC8vY2FsbCB0byB0aGUgbW9kdWxlIG5hbWUgKHdoaWNoIGlzIHN0b3JlZCBvbiB0aGUgbm9kZSksIGhvbGQgb24KICAgICAgICAgICAgLy90byBhIHJlZmVyZW5jZSB0byB0aGlzIG5vZGUsIGJ1dCBjbGVhciBhZnRlciB0aGUgRE9NIGluc2VydGlvbi4KICAgICAgICAgICAgY3VycmVudGx5QWRkaW5nU2NyaXB0ID0gbm9kZTsKICAgICAgICAgICAgaWYgKGJhc2VFbGVtZW50KSB7CiAgICAgICAgICAgICAgICBoZWFkLmluc2VydEJlZm9yZShub2RlLCBiYXNlRWxlbWVudCk7CiAgICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgICAgICBoZWFkLmFwcGVuZENoaWxkKG5vZGUpOwogICAgICAgICAgICB9CiAgICAgICAgICAgIGN1cnJlbnRseUFkZGluZ1NjcmlwdCA9IG51bGw7CgogICAgICAgICAgICByZXR1cm4gbm9kZTsKICAgICAgICB9IGVsc2UgaWYgKGlzV2ViV29ya2VyKSB7CiAgICAgICAgICAgIHRyeSB7CiAgICAgICAgICAgICAgICAvL0luIGEgd2ViIHdvcmtlciwgdXNlIGltcG9ydFNjcmlwdHMuIFRoaXMgaXMgbm90IGEgdmVyeQogICAgICAgICAgICAgICAgLy9lZmZpY2llbnQgdXNlIG9mIGltcG9ydFNjcmlwdHMsIGltcG9ydFNjcmlwdHMgd2lsbCBibG9jayB1bnRpbAogICAgICAgICAgICAgICAgLy9pdHMgc2NyaXB0IGlzIGRvd25sb2FkZWQgYW5kIGV2YWx1YXRlZC4gSG93ZXZlciwgaWYgd2ViIHdvcmtlcnMKICAgICAgICAgICAgICAgIC8vYXJlIGluIHBsYXksIHRoZSBleHBlY3RhdGlvbiBpcyB0aGF0IGEgYnVpbGQgaGFzIGJlZW4gZG9uZSBzbwogICAgICAgICAgICAgICAgLy90aGF0IG9ubHkgb25lIHNjcmlwdCBuZWVkcyB0byBiZSBsb2FkZWQgYW55d2F5LiBUaGlzIG1heSBuZWVkCiAgICAgICAgICAgICAgICAvL3RvIGJlIHJlZXZhbHVhdGVkIGlmIG90aGVyIHVzZSBjYXNlcyBiZWNvbWUgY29tbW9uLgogICAgICAgICAgICAgICAgaW1wb3J0U2NyaXB0cyh1cmwpOwoKICAgICAgICAgICAgICAgIC8vQWNjb3VudCBmb3IgYW5vbnltb3VzIG1vZHVsZXMKICAgICAgICAgICAgICAgIGNvbnRleHQuY29tcGxldGVMb2FkKG1vZHVsZU5hbWUpOwogICAgICAgICAgICB9IGNhdGNoIChlKSB7CiAgICAgICAgICAgICAgICBjb250ZXh0Lm9uRXJyb3IobWFrZUVycm9yKCdpbXBvcnRzY3JpcHRzJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnaW1wb3J0U2NyaXB0cyBmYWlsZWQgZm9yICcgKwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtb2R1bGVOYW1lICsgJyBhdCAnICsgdXJsLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgW21vZHVsZU5hbWVdKSk7CiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICB9OwoKICAgIGZ1bmN0aW9uIGdldEludGVyYWN0aXZlU2NyaXB0KCkgewogICAgICAgIGlmIChpbnRlcmFjdGl2ZVNjcmlwdCAmJiBpbnRlcmFjdGl2ZVNjcmlwdC5yZWFkeVN0YXRlID09PSAnaW50ZXJhY3RpdmUnKSB7CiAgICAgICAgICAgIHJldHVybiBpbnRlcmFjdGl2ZVNjcmlwdDsKICAgICAgICB9CgogICAgICAgIGVhY2hSZXZlcnNlKHNjcmlwdHMoKSwgZnVuY3Rpb24gKHNjcmlwdCkgewogICAgICAgICAgICBpZiAoc2NyaXB0LnJlYWR5U3RhdGUgPT09ICdpbnRlcmFjdGl2ZScpIHsKICAgICAgICAgICAgICAgIHJldHVybiAoaW50ZXJhY3RpdmVTY3JpcHQgPSBzY3JpcHQpOwogICAgICAgICAgICB9CiAgICAgICAgfSk7CiAgICAgICAgcmV0dXJuIGludGVyYWN0aXZlU2NyaXB0OwogICAgfQoKICAgIC8vTG9vayBmb3IgYSBkYXRhLW1haW4gc2NyaXB0IGF0dHJpYnV0ZSwgd2hpY2ggY291bGQgYWxzbyBhZGp1c3QgdGhlIGJhc2VVcmwuCiAgICBpZiAoaXNCcm93c2VyICYmICFjZmcuc2tpcERhdGFNYWluKSB7CiAgICAgICAgLy9GaWd1cmUgb3V0IGJhc2VVcmwuIEdldCBpdCBmcm9tIHRoZSBzY3JpcHQgdGFnIHdpdGggcmVxdWlyZS5qcyBpbiBpdC4KICAgICAgICBlYWNoUmV2ZXJzZShzY3JpcHRzKCksIGZ1bmN0aW9uIChzY3JpcHQpIHsKICAgICAgICAgICAgLy9TZXQgdGhlICdoZWFkJyB3aGVyZSB3ZSBjYW4gYXBwZW5kIGNoaWxkcmVuIGJ5CiAgICAgICAgICAgIC8vdXNpbmcgdGhlIHNjcmlwdCdzIHBhcmVudC4KICAgICAgICAgICAgaWYgKCFoZWFkKSB7CiAgICAgICAgICAgICAgICBoZWFkID0gc2NyaXB0LnBhcmVudE5vZGU7CiAgICAgICAgICAgIH0KCiAgICAgICAgICAgIC8vTG9vayBmb3IgYSBkYXRhLW1haW4gYXR0cmlidXRlIHRvIHNldCBtYWluIHNjcmlwdCBmb3IgdGhlIHBhZ2UKICAgICAgICAgICAgLy90byBsb2FkLiBJZiBpdCBpcyB0aGVyZSwgdGhlIHBhdGggdG8gZGF0YSBtYWluIGJlY29tZXMgdGhlCiAgICAgICAgICAgIC8vYmFzZVVybCwgaWYgaXQgaXMgbm90IGFscmVhZHkgc2V0LgogICAgICAgICAgICBkYXRhTWFpbiA9IHNjcmlwdC5nZXRBdHRyaWJ1dGUoJ2RhdGEtbWFpbicpOwogICAgICAgICAgICBpZiAoZGF0YU1haW4pIHsKICAgICAgICAgICAgICAgIC8vUHJlc2VydmUgZGF0YU1haW4gaW4gY2FzZSBpdCBpcyBhIHBhdGggKGkuZS4gY29udGFpbnMgJz8nKQogICAgICAgICAgICAgICAgbWFpblNjcmlwdCA9IGRhdGFNYWluOwoKICAgICAgICAgICAgICAgIC8vU2V0IGZpbmFsIGJhc2VVcmwgaWYgdGhlcmUgaXMgbm90IGFscmVhZHkgYW4gZXhwbGljaXQgb25lLgogICAgICAgICAgICAgICAgaWYgKCFjZmcuYmFzZVVybCkgewogICAgICAgICAgICAgICAgICAgIC8vUHVsbCBvZmYgdGhlIGRpcmVjdG9yeSBvZiBkYXRhLW1haW4gZm9yIHVzZSBhcyB0aGUKICAgICAgICAgICAgICAgICAgICAvL2Jhc2VVcmwuCiAgICAgICAgICAgICAgICAgICAgc3JjID0gbWFpblNjcmlwdC5zcGxpdCgnLycpOwogICAgICAgICAgICAgICAgICAgIG1haW5TY3JpcHQgPSBzcmMucG9wKCk7CiAgICAgICAgICAgICAgICAgICAgc3ViUGF0aCA9IHNyYy5sZW5ndGggPyBzcmMuam9pbignLycpICArICcvJyA6ICcuLyc7CgogICAgICAgICAgICAgICAgICAgIGNmZy5iYXNlVXJsID0gc3ViUGF0aDsKICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICAvL1N0cmlwIG9mZiBhbnkgdHJhaWxpbmcgLmpzIHNpbmNlIG1haW5TY3JpcHQgaXMgbm93CiAgICAgICAgICAgICAgICAvL2xpa2UgYSBtb2R1bGUgbmFtZS4KICAgICAgICAgICAgICAgIG1haW5TY3JpcHQgPSBtYWluU2NyaXB0LnJlcGxhY2UoanNTdWZmaXhSZWdFeHAsICcnKTsKCiAgICAgICAgICAgICAgICAvL0lmIG1haW5TY3JpcHQgaXMgc3RpbGwgYSBwYXRoLCBmYWxsIGJhY2sgdG8gZGF0YU1haW4KICAgICAgICAgICAgICAgIGlmIChyZXEuanNFeHRSZWdFeHAudGVzdChtYWluU2NyaXB0KSkgewogICAgICAgICAgICAgICAgICAgIG1haW5TY3JpcHQgPSBkYXRhTWFpbjsKICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICAvL1B1dCB0aGUgZGF0YS1tYWluIHNjcmlwdCBpbiB0aGUgZmlsZXMgdG8gbG9hZC4KICAgICAgICAgICAgICAgIGNmZy5kZXBzID0gY2ZnLmRlcHMgPyBjZmcuZGVwcy5jb25jYXQobWFpblNjcmlwdCkgOiBbbWFpblNjcmlwdF07CgogICAgICAgICAgICAgICAgcmV0dXJuIHRydWU7CiAgICAgICAgICAgIH0KICAgICAgICB9KTsKICAgIH0KCiAgICAvKioKICAgICAqIFRoZSBmdW5jdGlvbiB0aGF0IGhhbmRsZXMgZGVmaW5pdGlvbnMgb2YgbW9kdWxlcy4gRGlmZmVycyBmcm9tCiAgICAgKiByZXF1aXJlKCkgaW4gdGhhdCBhIHN0cmluZyBmb3IgdGhlIG1vZHVsZSBzaG91bGQgYmUgdGhlIGZpcnN0IGFyZ3VtZW50LAogICAgICogYW5kIHRoZSBmdW5jdGlvbiB0byBleGVjdXRlIGFmdGVyIGRlcGVuZGVuY2llcyBhcmUgbG9hZGVkIHNob3VsZAogICAgICogcmV0dXJuIGEgdmFsdWUgdG8gZGVmaW5lIHRoZSBtb2R1bGUgY29ycmVzcG9uZGluZyB0byB0aGUgZmlyc3QgYXJndW1lbnQncwogICAgICogbmFtZS4KICAgICAqLwogICAgZGVmaW5lID0gZnVuY3Rpb24gKG5hbWUsIGRlcHMsIGNhbGxiYWNrKSB7CiAgICAgICAgdmFyIG5vZGUsIGNvbnRleHQ7CgogICAgICAgIC8vQWxsb3cgZm9yIGFub255bW91cyBtb2R1bGVzCiAgICAgICAgaWYgKHR5cGVvZiBuYW1lICE9PSAnc3RyaW5nJykgewogICAgICAgICAgICAvL0FkanVzdCBhcmdzIGFwcHJvcHJpYXRlbHkKICAgICAgICAgICAgY2FsbGJhY2sgPSBkZXBzOwogICAgICAgICAgICBkZXBzID0gbmFtZTsKICAgICAgICAgICAgbmFtZSA9IG51bGw7CiAgICAgICAgfQoKICAgICAgICAvL1RoaXMgbW9kdWxlIG1heSBub3QgaGF2ZSBkZXBlbmRlbmNpZXMKICAgICAgICBpZiAoIWlzQXJyYXkoZGVwcykpIHsKICAgICAgICAgICAgY2FsbGJhY2sgPSBkZXBzOwogICAgICAgICAgICBkZXBzID0gbnVsbDsKICAgICAgICB9CgogICAgICAgIC8vSWYgbm8gbmFtZSwgYW5kIGNhbGxiYWNrIGlzIGEgZnVuY3Rpb24sIHRoZW4gZmlndXJlIG91dCBpZiBpdCBhCiAgICAgICAgLy9Db21tb25KUyB0aGluZyB3aXRoIGRlcGVuZGVuY2llcy4KICAgICAgICBpZiAoIWRlcHMgJiYgaXNGdW5jdGlvbihjYWxsYmFjaykpIHsKICAgICAgICAgICAgZGVwcyA9IFtdOwogICAgICAgICAgICAvL1JlbW92ZSBjb21tZW50cyBmcm9tIHRoZSBjYWxsYmFjayBzdHJpbmcsCiAgICAgICAgICAgIC8vbG9vayBmb3IgcmVxdWlyZSBjYWxscywgYW5kIHB1bGwgdGhlbSBpbnRvIHRoZSBkZXBlbmRlbmNpZXMsCiAgICAgICAgICAgIC8vYnV0IG9ubHkgaWYgdGhlcmUgYXJlIGZ1bmN0aW9uIGFyZ3MuCiAgICAgICAgICAgIGlmIChjYWxsYmFjay5sZW5ndGgpIHsKICAgICAgICAgICAgICAgIGNhbGxiYWNrCiAgICAgICAgICAgICAgICAgICAgLnRvU3RyaW5nKCkKICAgICAgICAgICAgICAgICAgICAucmVwbGFjZShjb21tZW50UmVnRXhwLCAnJykKICAgICAgICAgICAgICAgICAgICAucmVwbGFjZShjanNSZXF1aXJlUmVnRXhwLCBmdW5jdGlvbiAobWF0Y2gsIGRlcCkgewogICAgICAgICAgICAgICAgICAgICAgICBkZXBzLnB1c2goZGVwKTsKICAgICAgICAgICAgICAgICAgICB9KTsKCiAgICAgICAgICAgICAgICAvL01heSBiZSBhIENvbW1vbkpTIHRoaW5nIGV2ZW4gd2l0aG91dCByZXF1aXJlIGNhbGxzLCBidXQgc3RpbGwKICAgICAgICAgICAgICAgIC8vY291bGQgdXNlIGV4cG9ydHMsIGFuZCBtb2R1bGUuIEF2b2lkIGRvaW5nIGV4cG9ydHMgYW5kIG1vZHVsZQogICAgICAgICAgICAgICAgLy93b3JrIHRob3VnaCBpZiBpdCBqdXN0IG5lZWRzIHJlcXVpcmUuCiAgICAgICAgICAgICAgICAvL1JFUVVJUkVTIHRoZSBmdW5jdGlvbiB0byBleHBlY3QgdGhlIENvbW1vbkpTIHZhcmlhYmxlcyBpbiB0aGUKICAgICAgICAgICAgICAgIC8vb3JkZXIgbGlzdGVkIGJlbG93LgogICAgICAgICAgICAgICAgZGVwcyA9IChjYWxsYmFjay5sZW5ndGggPT09IDEgPyBbJ3JlcXVpcmUnXSA6IFsncmVxdWlyZScsICdleHBvcnRzJywgJ21vZHVsZSddKS5jb25jYXQoZGVwcyk7CiAgICAgICAgICAgIH0KICAgICAgICB9CgogICAgICAgIC8vSWYgaW4gSUUgNi04IGFuZCBoaXQgYW4gYW5vbnltb3VzIGRlZmluZSgpIGNhbGwsIGRvIHRoZSBpbnRlcmFjdGl2ZQogICAgICAgIC8vd29yay4KICAgICAgICBpZiAodXNlSW50ZXJhY3RpdmUpIHsKICAgICAgICAgICAgbm9kZSA9IGN1cnJlbnRseUFkZGluZ1NjcmlwdCB8fCBnZXRJbnRlcmFjdGl2ZVNjcmlwdCgpOwogICAgICAgICAgICBpZiAobm9kZSkgewogICAgICAgICAgICAgICAgaWYgKCFuYW1lKSB7CiAgICAgICAgICAgICAgICAgICAgbmFtZSA9IG5vZGUuZ2V0QXR0cmlidXRlKCdkYXRhLXJlcXVpcmVtb2R1bGUnKTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIGNvbnRleHQgPSBjb250ZXh0c1tub2RlLmdldEF0dHJpYnV0ZSgnZGF0YS1yZXF1aXJlY29udGV4dCcpXTsKICAgICAgICAgICAgfQogICAgICAgIH0KCiAgICAgICAgLy9BbHdheXMgc2F2ZSBvZmYgZXZhbHVhdGluZyB0aGUgZGVmIGNhbGwgdW50aWwgdGhlIHNjcmlwdCBvbmxvYWQgaGFuZGxlci4KICAgICAgICAvL1RoaXMgYWxsb3dzIG11bHRpcGxlIG1vZHVsZXMgdG8gYmUgaW4gYSBmaWxlIHdpdGhvdXQgcHJlbWF0dXJlbHkKICAgICAgICAvL3RyYWNpbmcgZGVwZW5kZW5jaWVzLCBhbmQgYWxsb3dzIGZvciBhbm9ueW1vdXMgbW9kdWxlIHN1cHBvcnQsCiAgICAgICAgLy93aGVyZSB0aGUgbW9kdWxlIG5hbWUgaXMgbm90IGtub3duIHVudGlsIHRoZSBzY3JpcHQgb25sb2FkIGV2ZW50CiAgICAgICAgLy9vY2N1cnMuIElmIG5vIGNvbnRleHQsIHVzZSB0aGUgZ2xvYmFsIHF1ZXVlLCBhbmQgZ2V0IGl0IHByb2Nlc3NlZAogICAgICAgIC8vaW4gdGhlIG9uc2NyaXB0IGxvYWQgY2FsbGJhY2suCiAgICAgICAgaWYgKGNvbnRleHQpIHsKICAgICAgICAgICAgY29udGV4dC5kZWZRdWV1ZS5wdXNoKFtuYW1lLCBkZXBzLCBjYWxsYmFja10pOwogICAgICAgICAgICBjb250ZXh0LmRlZlF1ZXVlTWFwW25hbWVdID0gdHJ1ZTsKICAgICAgICB9IGVsc2UgewogICAgICAgICAgICBnbG9iYWxEZWZRdWV1ZS5wdXNoKFtuYW1lLCBkZXBzLCBjYWxsYmFja10pOwogICAgICAgIH0KICAgIH07CgogICAgZGVmaW5lLmFtZCA9IHsKICAgICAgICBqUXVlcnk6IHRydWUKICAgIH07CgogICAgLyoqCiAgICAgKiBFeGVjdXRlcyB0aGUgdGV4dC4gTm9ybWFsbHkganVzdCB1c2VzIGV2YWwsIGJ1dCBjYW4gYmUgbW9kaWZpZWQKICAgICAqIHRvIHVzZSBhIGJldHRlciwgZW52aXJvbm1lbnQtc3BlY2lmaWMgY2FsbC4gT25seSB1c2VkIGZvciB0cmFuc3BpbGluZwogICAgICogbG9hZGVyIHBsdWdpbnMsIG5vdCBmb3IgcGxhaW4gSlMgbW9kdWxlcy4KICAgICAqIEBwYXJhbSB7U3RyaW5nfSB0ZXh0IHRoZSB0ZXh0IHRvIGV4ZWN1dGUvZXZhbHVhdGUuCiAgICAgKi8KICAgIHJlcS5leGVjID0gZnVuY3Rpb24gKHRleHQpIHsKICAgICAgICAvKmpzbGludCBldmlsOiB0cnVlICovCiAgICAgICAgcmV0dXJuIGV2YWwodGV4dCk7CiAgICB9OwoKICAgIC8vU2V0IHVwIHdpdGggY29uZmlnIGluZm8uCiAgICByZXEoY2ZnKTsKfSh0aGlzKSk7Cg==",
"ok": true,
"headers": [
[
"content-type",
"application/javascript"
]
],
"status": 200,
"status_text": ""
}
},
"base_uri": "https://localhost:8080/",
"height": 617
}
},
"source": [
"configure_plotly_browser_state()\n",
"perf_post_LASSO = backtestmodel.cumulative_return\n",
"mychart([perf_post_LASSO],[\"Post-LASSO\"], title=\"Post-LASSO\")\n"
],
"execution_count": null,
"outputs": [
{
"output_type": "display_data",
"data": {
"text/html": [
"\n",
" <script src=\"/static/components/requirejs/require.js\"></script>\n",
" <script>\n",
" requirejs.config({\n",
" paths: {\n",
" base: '/static/base',\n",
" plotly: 'https://cdn.plot.ly/plotly-latest.min.js?noext',\n",
" },\n",
" });\n",
" </script>\n",
" "
],
"text/plain": [
"<IPython.core.display.HTML object>"
]
},
"metadata": {}
},
{
"output_type": "display_data",
"data": {
"text/html": [
"<div id=\"4464abd1-65c9-450f-baaa-676aaaf05617\" style=\"height: 600px; width: 900px;\" class=\"plotly-graph-div\"></div><script type=\"text/javascript\">require([\"plotly\"], function(Plotly) { window.PLOTLYENV=window.PLOTLYENV || {};window.PLOTLYENV.BASE_URL=\"https://plot.ly\";Plotly.newPlot(\"4464abd1-65c9-450f-baaa-676aaaf05617\", [{\"type\": \"scatter\", \"x\": [1970.0, 1970.081850533808, 1970.1637010676156, 1970.2455516014236, 1970.3274021352313, 1970.4092526690392, 1970.491103202847, 1970.5729537366549, 1970.6548042704626, 1970.7366548042705, 1970.8185053380782, 1970.9003558718862, 1970.982206405694, 1971.0640569395018, 1971.1459074733095, 1971.2277580071175, 1971.3096085409252, 1971.3914590747331, 1971.4733096085408, 1971.5551601423488, 1971.6370106761565, 1971.7188612099644, 1971.8007117437724, 1971.88256227758, 1971.964412811388, 1972.0462633451957, 1972.1281138790036, 1972.2099644128114, 1972.2918149466193, 1972.373665480427, 1972.455516014235, 1972.5373665480427, 1972.6192170818506, 1972.7010676156583, 1972.7829181494662, 1972.864768683274, 1972.946619217082, 1973.0284697508896, 1973.1103202846975, 1973.1921708185052, 1973.2740213523132, 1973.355871886121, 1973.4377224199288, 1973.5195729537365, 1973.6014234875445, 1973.6832740213524, 1973.7651245551601, 1973.846975088968, 1973.9288256227758, 1974.0106761565837, 1974.0925266903914, 1974.1743772241994, 1974.256227758007, 1974.338078291815, 1974.4199288256227, 1974.5017793594307, 1974.5836298932384, 1974.6654804270463, 1974.747330960854, 1974.829181494662, 1974.9110320284697, 1974.9928825622776, 1975.0747330960853, 1975.1565836298932, 1975.238434163701, 1975.320284697509, 1975.4021352313168, 1975.4839857651245, 1975.5658362989325, 1975.6476868327402, 1975.7295373665481, 1975.8113879003558, 1975.8932384341638, 1975.9750889679715, 1976.0569395017794, 1976.1387900355871, 1976.220640569395, 1976.3024911032028, 1976.3843416370107, 1976.4661921708184, 1976.5480427046264, 1976.629893238434, 1976.711743772242, 1976.7935943060497, 1976.8754448398577, 1976.9572953736654, 1977.0391459074733, 1977.1209964412812, 1977.202846975089, 1977.284697508897, 1977.3665480427046, 1977.4483985765125, 1977.5302491103203, 1977.6120996441282, 1977.693950177936, 1977.7758007117438, 1977.8576512455516, 1977.9395017793595, 1978.0213523131672, 1978.1032028469751, 1978.1850533807828, 1978.2669039145908, 1978.3487544483985, 1978.4306049822064, 1978.5124555160141, 1978.594306049822, 1978.6761565836298, 1978.7580071174377, 1978.8398576512454, 1978.9217081850534, 1979.0035587188613, 1979.085409252669, 1979.167259786477, 1979.2491103202847, 1979.3309608540926, 1979.4128113879003, 1979.4946619217083, 1979.576512455516, 1979.658362989324, 1979.7402135231316, 1979.8220640569396, 1979.9039145907473, 1979.9857651245552, 1980.067615658363, 1980.1494661921708, 1980.2313167259786, 1980.3131672597865, 1980.3950177935942, 1980.4768683274021, 1980.5587188612099, 1980.6405693950178, 1980.7224199288257, 1980.8042704626334, 1980.8861209964414, 1980.967971530249, 1981.049822064057, 1981.1316725978647, 1981.2135231316727, 1981.2953736654804, 1981.3772241992883, 1981.459074733096, 1981.540925266904, 1981.6227758007117, 1981.7046263345196, 1981.7864768683273, 1981.8683274021353, 1981.950177935943, 1982.032028469751, 1982.1138790035586, 1982.1957295373666, 1982.2775800711743, 1982.3594306049822, 1982.4412811387901, 1982.5231316725979, 1982.6049822064058, 1982.6868327402135, 1982.7686832740214, 1982.8505338078292, 1982.932384341637, 1983.0142348754448, 1983.0960854092527, 1983.1779359430604, 1983.2597864768684, 1983.341637010676, 1983.423487544484, 1983.5053380782917, 1983.5871886120997, 1983.6690391459074, 1983.7508896797153, 1983.832740213523, 1983.914590747331, 1983.9964412811387, 1984.0782918149466, 1984.1601423487546, 1984.2419928825623, 1984.3238434163702, 1984.405693950178, 1984.4875444839859, 1984.5693950177936, 1984.6512455516015, 1984.7330960854092, 1984.8149466192172, 1984.8967971530249, 1984.9786476868328, 1985.0604982206405, 1985.1423487544484, 1985.2241992882562, 1985.306049822064, 1985.3879003558718, 1985.4697508896797, 1985.5516014234875, 1985.6334519572954, 1985.715302491103, 1985.797153024911, 1985.8790035587188, 1985.9608540925267, 1986.0427046263346, 1986.1245551601423, 1986.2064056939503, 1986.288256227758, 1986.370106761566, 1986.4519572953736, 1986.5338078291816, 1986.6156583629893, 1986.6975088967972, 1986.779359430605, 1986.8612099644129, 1986.9430604982206, 1987.0249110320285, 1987.1067615658362, 1987.1886120996442, 1987.2704626334519, 1987.3523131672598, 1987.4341637010675, 1987.5160142348755, 1987.5978647686832, 1987.679715302491, 1987.761565836299, 1987.8434163701068, 1987.9252669039147, 1988.0071174377224, 1988.0889679715303, 1988.170818505338, 1988.252669039146, 1988.3345195729537, 1988.4163701067616, 1988.4982206405693, 1988.5800711743773, 1988.661921708185, 1988.743772241993, 1988.8256227758006, 1988.9074733096086, 1988.9893238434163, 1989.0711743772242, 1989.153024911032, 1989.2348754448399, 1989.3167259786476, 1989.3985765124555, 1989.4804270462632, 1989.5622775800712, 1989.644128113879, 1989.7259786476868, 1989.8078291814948, 1989.8896797153025, 1989.9715302491104, 1990.053380782918, 1990.135231316726, 1990.2170818505338, 1990.2989323843417, 1990.3807829181494, 1990.4626334519573, 1990.544483985765, 1990.626334519573, 1990.7081850533807, 1990.7900355871886, 1990.8718861209964, 1990.9537366548043, 1991.035587188612, 1991.11743772242, 1991.1992882562276, 1991.2811387900356, 1991.3629893238435, 1991.4448398576512, 1991.5266903914592, 1991.6085409252669, 1991.6903914590748, 1991.7722419928825, 1991.8540925266905, 1991.9359430604982, 1992.017793594306, 1992.0996441281138, 1992.1814946619218, 1992.2633451957295, 1992.3451957295374, 1992.4270462633451, 1992.508896797153, 1992.5907473309608, 1992.6725978647687, 1992.7544483985764, 1992.8362989323844, 1992.918149466192, 1993.0, 1993.081850533808, 1993.1637010676156, 1993.2455516014236, 1993.3274021352313, 1993.4092526690392, 1993.491103202847, 1993.5729537366549, 1993.6548042704626, 1993.7366548042705, 1993.8185053380782, 1993.9003558718862, 1993.982206405694, 1994.0640569395018, 1994.1459074733095, 1994.2277580071175, 1994.3096085409252, 1994.3914590747331, 1994.4733096085408, 1994.5551601423488, 1994.6370106761565, 1994.7188612099644, 1994.8007117437724, 1994.88256227758, 1994.964412811388, 1995.0462633451957, 1995.1281138790036, 1995.2099644128114, 1995.2918149466193, 1995.373665480427, 1995.455516014235, 1995.5373665480427, 1995.6192170818506, 1995.7010676156583, 1995.7829181494662, 1995.864768683274, 1995.946619217082, 1996.0284697508896, 1996.1103202846975, 1996.1921708185052, 1996.2740213523132, 1996.355871886121, 1996.4377224199288, 1996.5195729537368, 1996.6014234875445, 1996.6832740213524, 1996.7651245551601, 1996.846975088968, 1996.9288256227758, 1997.0106761565837, 1997.0925266903914, 1997.1743772241994, 1997.256227758007, 1997.338078291815, 1997.4199288256227, 1997.5017793594307, 1997.5836298932384, 1997.6654804270463, 1997.747330960854, 1997.829181494662, 1997.9110320284697, 1997.9928825622776, 1998.0747330960853, 1998.1565836298932, 1998.238434163701, 1998.320284697509, 1998.4021352313168, 1998.4839857651245, 1998.5658362989325, 1998.6476868327402, 1998.7295373665481, 1998.8113879003558, 1998.8932384341638, 1998.9750889679715, 1999.0569395017794, 1999.1387900355871, 1999.220640569395, 1999.3024911032028, 1999.3843416370107, 1999.4661921708184, 1999.5480427046264, 1999.629893238434, 1999.711743772242, 1999.7935943060497, 1999.8754448398577, 1999.9572953736654, 2000.0391459074733, 2000.1209964412812, 2000.202846975089, 2000.284697508897, 2000.3665480427046, 2000.4483985765125, 2000.5302491103203, 2000.6120996441282, 2000.693950177936, 2000.7758007117438, 2000.8576512455516, 2000.9395017793595, 2001.0213523131672, 2001.1032028469751, 2001.1850533807828, 2001.2669039145908, 2001.3487544483985, 2001.4306049822064, 2001.5124555160141, 2001.594306049822, 2001.6761565836298, 2001.7580071174377, 2001.8398576512454, 2001.9217081850534, 2002.0035587188613, 2002.085409252669, 2002.167259786477, 2002.2491103202847, 2002.3309608540926, 2002.4128113879003, 2002.4946619217083, 2002.576512455516, 2002.658362989324, 2002.7402135231316, 2002.8220640569396, 2002.9039145907473, 2002.9857651245552, 2003.067615658363, 2003.1494661921708, 2003.2313167259786, 2003.3131672597865, 2003.3950177935942, 2003.4768683274021, 2003.5587188612099, 2003.6405693950178, 2003.7224199288257, 2003.8042704626334, 2003.8861209964414, 2003.967971530249, 2004.049822064057, 2004.1316725978647, 2004.2135231316727, 2004.2953736654804, 2004.3772241992883, 2004.459074733096, 2004.540925266904, 2004.6227758007117, 2004.7046263345196, 2004.7864768683273, 2004.8683274021353, 2004.950177935943, 2005.032028469751, 2005.1138790035586, 2005.1957295373666, 2005.2775800711743, 2005.3594306049822, 2005.4412811387901, 2005.5231316725979, 2005.6049822064058, 2005.6868327402135, 2005.7686832740214, 2005.8505338078292, 2005.932384341637, 2006.0142348754448, 2006.0960854092527, 2006.1779359430604, 2006.2597864768684, 2006.341637010676, 2006.423487544484, 2006.5053380782917, 2006.5871886120997, 2006.6690391459074, 2006.7508896797153, 2006.832740213523, 2006.914590747331, 2006.9964412811387, 2007.0782918149466, 2007.1601423487546, 2007.2419928825623, 2007.3238434163702, 2007.405693950178, 2007.4875444839859, 2007.5693950177936, 2007.6512455516015, 2007.7330960854092, 2007.8149466192172, 2007.8967971530249, 2007.9786476868328, 2008.0604982206405, 2008.1423487544484, 2008.2241992882562, 2008.306049822064, 2008.3879003558718, 2008.4697508896797, 2008.5516014234875, 2008.6334519572954, 2008.715302491103, 2008.797153024911, 2008.879003558719, 2008.9608540925267, 2009.0427046263346, 2009.1245551601423, 2009.2064056939503, 2009.288256227758, 2009.370106761566, 2009.4519572953736, 2009.5338078291816, 2009.6156583629893, 2009.6975088967972, 2009.779359430605, 2009.8612099644129, 2009.9430604982206, 2010.0249110320285, 2010.1067615658362, 2010.1886120996442, 2010.2704626334519, 2010.3523131672598, 2010.4341637010675, 2010.5160142348755, 2010.5978647686832, 2010.679715302491, 2010.761565836299, 2010.8434163701068, 2010.9252669039147, 2011.0071174377224, 2011.0889679715303, 2011.170818505338, 2011.252669039146, 2011.3345195729537, 2011.4163701067616, 2011.4982206405693, 2011.5800711743773, 2011.661921708185, 2011.743772241993, 2011.8256227758006, 2011.9074733096086, 2011.9893238434163, 2012.0711743772242, 2012.153024911032, 2012.2348754448399, 2012.3167259786476, 2012.3985765124555, 2012.4804270462632, 2012.5622775800712, 2012.644128113879, 2012.7259786476868, 2012.8078291814948, 2012.8896797153025, 2012.9715302491104, 2013.053380782918, 2013.135231316726, 2013.2170818505338, 2013.2989323843417, 2013.3807829181494, 2013.4626334519573, 2013.544483985765, 2013.626334519573, 2013.7081850533807, 2013.7900355871886, 2013.8718861209964, 2013.9537366548043, 2014.035587188612, 2014.11743772242, 2014.1992882562276, 2014.2811387900356, 2014.3629893238435, 2014.4448398576512, 2014.5266903914592, 2014.6085409252669, 2014.6903914590748, 2014.7722419928825, 2014.8540925266905, 2014.9359430604982, 2015.017793594306, 2015.0996441281138, 2015.1814946619218, 2015.2633451957295, 2015.3451957295374, 2015.4270462633451, 2015.508896797153, 2015.5907473309608, 2015.6725978647687, 2015.7544483985764, 2015.8362989323844, 2015.918149466192, 2016.0], \"y\": [100.0, 100.66, 99.779225, 104.53204208416666, 108.512970686872, 111.42292685079163, 110.51482999695767, 112.00217541733339, 114.36075456133008, 113.1409065126759, 113.24650469208773, 111.9743689560466, 114.68788116374813, 116.01826058524762, 118.00507329777, 116.34120176427143, 117.11777928604793, 118.92822495751143, 120.08777515084716, 117.4578528750436, 118.25362982827201, 118.34034915681274, 118.99122107717521, 117.07447082432371, 119.30669073470747, 119.07503691019758, 119.18121215144252, 120.56868009623891, 121.21774149075699, 121.53088732294147, 121.36074408068936, 124.07922474809678, 122.91288003546468, 124.59371366994966, 125.9071390682204, 124.47074845668378, 122.98021124391501, 127.80001068958278, 129.33787081821407, 132.2479729116239, 134.6240281582694, 139.1788077776242, 135.57987577317445, 135.7256241396306, 137.7173976738797, 139.923171326623, 147.11055822710054, 153.10653939617345, 149.0989757274786, 150.09296889899514, 147.56390237304706, 149.25965755115067, 149.3977227343855, 149.00430873118492, 152.59282916646094, 155.13095655826308, 157.9491689357382, 168.15663397821027, 169.91106819271624, 174.4052159464136, 160.6911524658273, 165.10480278688868, 166.57285965833543, 169.5600662748749, 175.13435345366145, 174.483437439992, 174.75097871073334, 175.05096789085343, 176.34780381131148, 172.25506519785728, 175.15756304644117, 172.8629989705328, 171.22656258027843, 173.11290854470448, 172.41180126509843, 168.5799489819816, 172.75230271928567, 174.34306350682573, 175.1392301635069, 176.0295212501714, 176.56787820266152, 176.45458048081483, 179.6969333971498, 179.05152191136503, 179.11866623208178, 179.65303691967415, 179.98988636389856, 179.53991164798884, 179.910960798728, 181.36524106518436, 184.11745859834855, 186.1688339495651, 185.51724303074164, 185.97949016129323, 184.10574679791821, 185.7534932317596, 184.9593970481938, 185.24454278530976, 184.75673215597513, 186.10237702184446, 187.9509939669281, 189.5376136076656, 187.77175484088752, 191.3832315923273, 189.13926320190726, 193.1568963844211, 191.97542003486973, 192.94009652054496, 194.5189896437381, 195.04743289893693, 196.50216166930818, 195.71451550461705, 194.27275190706638, 197.657954609047, 200.94401810442238, 203.27831778140205, 202.79722576265274, 201.4621440263819, 205.00787776124625, 204.05629952863782, 213.70816249634234, 216.4204752586918, 211.27868546733737, 217.73500996407674, 213.8901725797944, 213.08273717830568, 218.67615902923623, 220.65882287110134, 223.97054403769175, 225.47487952514493, 234.52581698075014, 233.3219177869156, 232.5675102527379, 236.4494496113732, 240.38239212324237, 241.00938952936383, 242.2325121812253, 240.85582407032865, 247.4572807817229, 248.9234651703546, 248.6994340517013, 249.22584785377745, 246.23721456159754, 248.1845405334222, 250.22999478831852, 253.08261672890535, 256.3262922666475, 259.72902379648724, 260.92377730595103, 260.93682349481634, 264.98569320604423, 263.91470936266984, 268.7575442794748, 275.4115331465942, 282.8453494454427, 274.5014116368021, 277.9624169351895, 271.6526700707607, 271.0278689295979, 272.744378766152, 275.45136672540605, 274.19117672263735, 270.0029064981991, 277.4549867175494, 274.63419435258766, 276.02338565235453, 273.41956504770064, 273.14386698627754, 281.96186149215123, 286.2923257482349, 284.58650064065165, 283.54538835914127, 285.7026961889071, 285.6455556496693, 285.82646450158074, 282.09404725263096, 279.9195723050586, 282.6557861243405, 283.72281171695994, 283.14354430970445, 283.07511795316293, 281.6314348516018, 290.50751890667476, 292.6742208185204, 291.1815822923459, 294.8213520710002, 289.63249627455065, 290.97204656982046, 289.5002129675881, 287.1383737301275, 292.3116500968319, 297.5610801464875, 298.5306333326315, 307.7353278603876, 310.7972943725984, 313.29403263739164, 313.57338648316, 317.6158700572388, 320.9455430950055, 323.772538420434, 328.75863551210875, 322.2108593548259, 322.7398221822668, 320.97013215730067, 319.1245538973962, 330.69015960489435, 323.1476682145727, 327.00389705526663, 328.23833676665026, 326.2853186628887, 316.96715377074105, 313.4673081145225, 316.8501428145917, 319.23708055712837, 322.32037869350927, 318.33435001033286, 315.78502242400015, 311.3403482333824, 313.1668782763515, 316.4081554665118, 318.68365745124174, 315.4410512366754, 313.24347857972657, 316.5351454671352, 315.7569965678618, 314.4045040992295, 316.56865510244586, 311.90981972818815, 312.2815122633642, 311.5996976282559, 311.7996407675674, 314.4499377140917, 318.37532110322263, 324.1007706277289, 322.839478462036, 318.4111969491317, 314.52923377299356, 315.2552720876196, 318.6127407353527, 318.2888177822718, 325.91183496815717, 327.3621426337655, 328.94984902553927, 332.80130350788, 328.9962752711065, 334.8551506065595, 338.0613886736173, 343.850689954653, 359.13485312313736, 362.0857444996325, 367.05838872409413, 370.5117963980066, 374.6924045006975, 379.66644617044415, 380.60928451176744, 374.0025416814507, 369.10310838542364, 364.67694694403514, 362.0847016461746, 368.5267919629628, 369.34369301848074, 365.2347444336502, 360.68148461971066, 365.45750861188327, 379.93267143215223, 382.5827018153915, 384.6072019458313, 385.0751407081988, 387.4016363499775, 394.1230547406496, 391.955377939576, 394.9864995289754, 395.0523306122302, 394.1865075876384, 405.5784976569211, 403.050391688193, 407.11784189097966, 409.4960886173594, 409.40395199742056, 405.4259102638456, 411.67960492966546, 412.2147884160741, 412.9876911443543, 412.0756766597439, 413.6759038707726, 414.7859342128258, 415.4012000152415, 411.8737514917787, 409.4231026704026, 408.5633141547948, 406.99375008958344, 409.4730203505458, 405.96861375137905, 405.70811722422195, 407.1247147335299, 405.8287010582949, 403.9517433159003, 397.2192142606353, 403.6409248911822, 398.57523128379785, 404.47414470679803, 405.78531505922257, 400.94294363284916, 401.24030964937685, 404.3900460801244, 404.1137128819696, 399.3990528983466, 393.544528447945, 390.8061144374948, 402.35117840150235, 404.93963764921864, 395.40668367955993, 400.59969145855155, 403.81784231326856, 410.7332228628833, 411.499924878894, 417.816448725785, 415.0553783604554, 416.5253661588154, 408.3996504740005, 415.8121041301036, 413.123185856729, 419.6746310457736, 413.1766688417482, 412.40540572657693, 418.2787460464662, 415.29502432466813, 419.0257579598514, 425.72318632457626, 428.462005489931, 429.72953892283874, 436.71264393033493, 435.107724963891, 442.9106568315768, 456.19797653652404, 464.20044937493554, 472.14601373340315, 474.88052606294247, 471.2437327008438, 476.3802893872829, 473.6768312450101, 484.20824612635744, 489.93804370551936, 502.2354886025279, 516.9258766441518, 526.3425430303528, 535.6631922298486, 559.1297039094512, 585.3855029221993, 573.8485303021076, 595.6643385957593, 591.8868339151646, 587.102415341017, 586.3293971608181, 588.288714562997, 605.2265271364568, 609.8312922970866, 634.7733921520374, 647.8074058042259, 647.4889004963723, 696.174670072862, 740.4919892785836, 723.4174781591349, 743.2391170606952, 793.531630648469, 787.540466837073, 784.0490374340955, 810.4845574795818, 826.9981803382283, 853.0830812763965, 882.2443046046945, 906.498670945452, 913.4258316225935, 911.6979344244407, 934.3840180260356, 955.1740624271151, 944.0622041675463, 965.7126973831218, 1004.4136337307505, 1044.0042711269707, 1072.1401862338428, 1049.1338447375747, 1029.0953883030872, 1042.4822041459297, 1036.3228717897675, 1081.8951700767225, 1069.4082966554204, 1047.5032500455952, 1053.5962272833603, 1086.5737891973297, 1094.7230926163097, 1104.5391096801027, 1109.7580569733411, 1131.1671394891184, 1139.41523321456, 1133.737147302374, 1121.5967120166777, 1132.401427009105, 1113.5563799279619, 1102.1145881242019, 1077.9139886266414, 1086.4834048362231, 1067.053459946402, 1072.3353745731365, 1065.8030649163618, 1068.7162599604665, 1071.842255020851, 1061.3024728464793, 1076.036888844498, 1078.2427644666293, 1077.7575552226192, 1074.8296471975978, 1074.596767440705, 1087.9128123839075, 1101.8380963824216, 1105.0885187667498, 1093.0706811251612, 1082.5407668969888, 1085.075716526139, 1074.1616632774137, 1071.8343130069793, 1111.6440257825802, 1115.0252763610022, 1131.1188078498126, 1132.4195944788398, 1139.950184782124, 1144.2059988053109, 1155.190376393842, 1189.143346873351, 1176.6077940917278, 1180.8239720205563, 1180.9518946175253, 1194.4147462161652, 1190.990757277012, 1163.240672632458, 1194.0471631126743, 1194.8033929826454, 1204.0929893630857, 1249.7582159846806, 1266.6820251594731, 1289.3345220427416, 1279.3421794969104, 1287.8178214360776, 1288.7192939110828, 1302.4548937186853, 1308.7175309993158, 1310.4406757484649, 1306.804202873263, 1310.5285948514518, 1325.9163847693326, 1344.1477350599107, 1358.2612862780397, 1335.5670039531442, 1317.2363468238873, 1292.1759253255627, 1323.0804662062658, 1286.8060100911107, 1291.8352769138835, 1286.2373240472568, 1247.9610450124837, 1256.197587909566, 1316.2124276719455, 1283.603264776373, 1319.0093214964547, 1372.3742402953321, 1375.473518787999, 1416.8179603069013, 1426.4523224369882, 1421.7688039783202, 1490.8312236315671, 1459.9461701153332, 1650.7368021132384, 1657.766189662237, 1638.6189901716382, 1625.728520782288, 1640.5091025837337, 1647.330886268644, 1648.2781015282485, 1671.010603678492, 1717.2836723120224, 1725.14024511285, 1724.162665640619, 1707.3808156950502, 1755.4720420037943, 1741.9987940814149, 1798.9766713044946, 1771.602242956145, 1756.0859599782539, 1735.3641456505106, 1740.3822403050167, 1782.165917257673, 1800.6855914145087, 1836.5942632499668, 1826.4011650889295, 1819.597820748973, 1802.4784379187597, 1848.7871131196225, 1849.3263426942822, 1885.6193721696575, 1939.972350572448, 2063.5647557401676, 1966.0269282855159, 1952.7070958463814, 1995.6015617184737, 1992.8409795580965, 2001.2773397048925, 2031.863528380049, 2020.8237365425173, 1999.857690275889, 1975.9093944348351, 1983.2367251058645, 1981.4518120532694, 1986.075199614727, 2008.666805010344, 1980.7798142007837, 1958.6611062755417, 1955.3477045707589, 1980.2620932398313, 1994.420967206496, 2044.6138948811931, 1999.2916202113267, 1946.560303728253, 1940.4124174356448, 1952.2974434924379, 1928.886143315891, 1924.1442982135727, 1917.3135859549145, 1905.3942864955616, 1923.8130979316854, 1920.318170803776, 1898.65058077654, 1903.349740963962, 1894.0391884810801, 1891.1034277389344, 1898.8727109878955, 1891.957649532048, 1937.1439047283716, 1957.031915483583, 1982.0003810052945, 1976.8471800146806, 2017.6690742819837, 2007.7993097269546, 1984.2411311594915, 1978.2553370804937, 1987.9487882321878, 2038.9231084104413, 2136.502570173784, 2109.1731414636447, 2064.2653299933145, 2110.418862329748, 2101.1857798070555, 2098.7343963972803, 2162.1336646217815, 2112.494679238173, 2132.492962201628, 2204.7133905215233, 2203.3538172640347, 2167.5860402971152, 2085.5970983228767, 2093.226907707575, 2082.69099893878, 2095.1524334157634, 2066.0298145912843], \"mode\": \"line\", \"name\": \"Post-LASSO\"}], {\"title\": \"Post-LASSO\", \"autosize\": false, \"width\": 900, \"height\": 600, \"yaxis\": {\"type\": \"log\", \"autorange\": true}}, {\"showLink\": true, \"linkText\": \"Export to plot.ly\"})});</script>"
],
"text/plain": [
"<IPython.core.display.HTML object>"
]
},
"metadata": {}
}
]
},
{
"cell_type": "code",
"metadata": {
"scrolled": false,
"id": "mmuwIkGzfkZd",
"outputId": "d43cd4e9-e1cb-4ab7-ee2e-5b01552d2129",
"colab": {
"resources": {
"http://localhost:8080/static/components/requirejs/require.js": {
"data": "LyoqIHZpbTogZXQ6dHM9NDpzdz00OnN0cz00CiAqIEBsaWNlbnNlIFJlcXVpcmVKUyAyLjEuMjIgQ29weXJpZ2h0IChjKSAyMDEwLTIwMTUsIFRoZSBEb2pvIEZvdW5kYXRpb24gQWxsIFJpZ2h0cyBSZXNlcnZlZC4KICogQXZhaWxhYmxlIHZpYSB0aGUgTUlUIG9yIG5ldyBCU0QgbGljZW5zZS4KICogc2VlOiBodHRwOi8vZ2l0aHViLmNvbS9qcmJ1cmtlL3JlcXVpcmVqcyBmb3IgZGV0YWlscwogKi8KLy9Ob3QgdXNpbmcgc3RyaWN0OiB1bmV2ZW4gc3RyaWN0IHN1cHBvcnQgaW4gYnJvd3NlcnMsICMzOTIsIGFuZCBjYXVzZXMKLy9wcm9ibGVtcyB3aXRoIHJlcXVpcmVqcy5leGVjKCkvdHJhbnNwaWxlciBwbHVnaW5zIHRoYXQgbWF5IG5vdCBiZSBzdHJpY3QuCi8qanNsaW50IHJlZ2V4cDogdHJ1ZSwgbm9tZW46IHRydWUsIHNsb3BweTogdHJ1ZSAqLwovKmdsb2JhbCB3aW5kb3csIG5hdmlnYXRvciwgZG9jdW1lbnQsIGltcG9ydFNjcmlwdHMsIHNldFRpbWVvdXQsIG9wZXJhICovCgp2YXIgcmVxdWlyZWpzLCByZXF1aXJlLCBkZWZpbmU7CihmdW5jdGlvbiAoZ2xvYmFsKSB7CiAgICB2YXIgcmVxLCBzLCBoZWFkLCBiYXNlRWxlbWVudCwgZGF0YU1haW4sIHNyYywKICAgICAgICBpbnRlcmFjdGl2ZVNjcmlwdCwgY3VycmVudGx5QWRkaW5nU2NyaXB0LCBtYWluU2NyaXB0LCBzdWJQYXRoLAogICAgICAgIHZlcnNpb24gPSAnMi4xLjIyJywKICAgICAgICBjb21tZW50UmVnRXhwID0gLyhcL1wqKFtcc1xTXSo/KVwqXC98KFteOl18XilcL1wvKC4qKSQpL21nLAogICAgICAgIGNqc1JlcXVpcmVSZWdFeHAgPSAvW14uXVxzKnJlcXVpcmVccypcKFxzKlsiJ10oW14nIlxzXSspWyInXVxzKlwpL2csCiAgICAgICAganNTdWZmaXhSZWdFeHAgPSAvXC5qcyQvLAogICAgICAgIGN1cnJEaXJSZWdFeHAgPSAvXlwuXC8vLAogICAgICAgIG9wID0gT2JqZWN0LnByb3RvdHlwZSwKICAgICAgICBvc3RyaW5nID0gb3AudG9TdHJpbmcsCiAgICAgICAgaGFzT3duID0gb3AuaGFzT3duUHJvcGVydHksCiAgICAgICAgYXAgPSBBcnJheS5wcm90b3R5cGUsCiAgICAgICAgaXNCcm93c2VyID0gISEodHlwZW9mIHdpbmRvdyAhPT0gJ3VuZGVmaW5lZCcgJiYgdHlwZW9mIG5hdmlnYXRvciAhPT0gJ3VuZGVmaW5lZCcgJiYgd2luZG93LmRvY3VtZW50KSwKICAgICAgICBpc1dlYldvcmtlciA9ICFpc0Jyb3dzZXIgJiYgdHlwZW9mIGltcG9ydFNjcmlwdHMgIT09ICd1bmRlZmluZWQnLAogICAgICAgIC8vUFMzIGluZGljYXRlcyBsb2FkZWQgYW5kIGNvbXBsZXRlLCBidXQgbmVlZCB0byB3YWl0IGZvciBjb21wbGV0ZQogICAgICAgIC8vc3BlY2lmaWNhbGx5LiBTZXF1ZW5jZSBpcyAnbG9hZGluZycsICdsb2FkZWQnLCBleGVjdXRpb24sCiAgICAgICAgLy8gdGhlbiAnY29tcGxldGUnLiBUaGUgVUEgY2hlY2sgaXMgdW5mb3J0dW5hdGUsIGJ1dCBub3Qgc3VyZSBob3cKICAgICAgICAvL3RvIGZlYXR1cmUgdGVzdCB3L28gY2F1c2luZyBwZXJmIGlzc3Vlcy4KICAgICAgICByZWFkeVJlZ0V4cCA9IGlzQnJvd3NlciAmJiBuYXZpZ2F0b3IucGxhdGZvcm0gPT09ICdQTEFZU1RBVElPTiAzJyA/CiAgICAgICAgICAgICAgICAgICAgICAvXmNvbXBsZXRlJC8gOiAvXihjb21wbGV0ZXxsb2FkZWQpJC8sCiAgICAgICAgZGVmQ29udGV4dE5hbWUgPSAnXycsCiAgICAgICAgLy9PaCB0aGUgdHJhZ2VkeSwgZGV0ZWN0aW5nIG9wZXJhLiBTZWUgdGhlIHVzYWdlIG9mIGlzT3BlcmEgZm9yIHJlYXNvbi4KICAgICAgICBpc09wZXJhID0gdHlwZW9mIG9wZXJhICE9PSAndW5kZWZpbmVkJyAmJiBvcGVyYS50b1N0cmluZygpID09PSAnW29iamVjdCBPcGVyYV0nLAogICAgICAgIGNvbnRleHRzID0ge30sCiAgICAgICAgY2ZnID0ge30sCiAgICAgICAgZ2xvYmFsRGVmUXVldWUgPSBbXSwKICAgICAgICB1c2VJbnRlcmFjdGl2ZSA9IGZhbHNlOwoKICAgIGZ1bmN0aW9uIGlzRnVuY3Rpb24oaXQpIHsKICAgICAgICByZXR1cm4gb3N0cmluZy5jYWxsKGl0KSA9PT0gJ1tvYmplY3QgRnVuY3Rpb25dJzsKICAgIH0KCiAgICBmdW5jdGlvbiBpc0FycmF5KGl0KSB7CiAgICAgICAgcmV0dXJuIG9zdHJpbmcuY2FsbChpdCkgPT09ICdbb2JqZWN0IEFycmF5XSc7CiAgICB9CgogICAgLyoqCiAgICAgKiBIZWxwZXIgZnVuY3Rpb24gZm9yIGl0ZXJhdGluZyBvdmVyIGFuIGFycmF5LiBJZiB0aGUgZnVuYyByZXR1cm5zCiAgICAgKiBhIHRydWUgdmFsdWUsIGl0IHdpbGwgYnJlYWsgb3V0IG9mIHRoZSBsb29wLgogICAgICovCiAgICBmdW5jdGlvbiBlYWNoKGFyeSwgZnVuYykgewogICAgICAgIGlmIChhcnkpIHsKICAgICAgICAgICAgdmFyIGk7CiAgICAgICAgICAgIGZvciAoaSA9IDA7IGkgPCBhcnkubGVuZ3RoOyBpICs9IDEpIHsKICAgICAgICAgICAgICAgIGlmIChhcnlbaV0gJiYgZnVuYyhhcnlbaV0sIGksIGFyeSkpIHsKICAgICAgICAgICAgICAgICAgICBicmVhazsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgIH0KICAgIH0KCiAgICAvKioKICAgICAqIEhlbHBlciBmdW5jdGlvbiBmb3IgaXRlcmF0aW5nIG92ZXIgYW4gYXJyYXkgYmFja3dhcmRzLiBJZiB0aGUgZnVuYwogICAgICogcmV0dXJucyBhIHRydWUgdmFsdWUsIGl0IHdpbGwgYnJlYWsgb3V0IG9mIHRoZSBsb29wLgogICAgICovCiAgICBmdW5jdGlvbiBlYWNoUmV2ZXJzZShhcnksIGZ1bmMpIHsKICAgICAgICBpZiAoYXJ5KSB7CiAgICAgICAgICAgIHZhciBpOwogICAgICAgICAgICBmb3IgKGkgPSBhcnkubGVuZ3RoIC0gMTsgaSA+IC0xOyBpIC09IDEpIHsKICAgICAgICAgICAgICAgIGlmIChhcnlbaV0gJiYgZnVuYyhhcnlbaV0sIGksIGFyeSkpIHsKICAgICAgICAgICAgICAgICAgICBicmVhazsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgIH0KICAgIH0KCiAgICBmdW5jdGlvbiBoYXNQcm9wKG9iaiwgcHJvcCkgewogICAgICAgIHJldHVybiBoYXNPd24uY2FsbChvYmosIHByb3ApOwogICAgfQoKICAgIGZ1bmN0aW9uIGdldE93bihvYmosIHByb3ApIHsKICAgICAgICByZXR1cm4gaGFzUHJvcChvYmosIHByb3ApICYmIG9ialtwcm9wXTsKICAgIH0KCiAgICAvKioKICAgICAqIEN5Y2xlcyBvdmVyIHByb3BlcnRpZXMgaW4gYW4gb2JqZWN0IGFuZCBjYWxscyBhIGZ1bmN0aW9uIGZvciBlYWNoCiAgICAgKiBwcm9wZXJ0eSB2YWx1ZS4gSWYgdGhlIGZ1bmN0aW9uIHJldHVybnMgYSB0cnV0aHkgdmFsdWUsIHRoZW4gdGhlCiAgICAgKiBpdGVyYXRpb24gaXMgc3RvcHBlZC4KICAgICAqLwogICAgZnVuY3Rpb24gZWFjaFByb3Aob2JqLCBmdW5jKSB7CiAgICAgICAgdmFyIHByb3A7CiAgICAgICAgZm9yIChwcm9wIGluIG9iaikgewogICAgICAgICAgICBpZiAoaGFzUHJvcChvYmosIHByb3ApKSB7CiAgICAgICAgICAgICAgICBpZiAoZnVuYyhvYmpbcHJvcF0sIHByb3ApKSB7CiAgICAgICAgICAgICAgICAgICAgYnJlYWs7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICB9CgogICAgLyoqCiAgICAgKiBTaW1wbGUgZnVuY3Rpb24gdG8gbWl4IGluIHByb3BlcnRpZXMgZnJvbSBzb3VyY2UgaW50byB0YXJnZXQsCiAgICAgKiBidXQgb25seSBpZiB0YXJnZXQgZG9lcyBub3QgYWxyZWFkeSBoYXZlIGEgcHJvcGVydHkgb2YgdGhlIHNhbWUgbmFtZS4KICAgICAqLwogICAgZnVuY3Rpb24gbWl4aW4odGFyZ2V0LCBzb3VyY2UsIGZvcmNlLCBkZWVwU3RyaW5nTWl4aW4pIHsKICAgICAgICBpZiAoc291cmNlKSB7CiAgICAgICAgICAgIGVhY2hQcm9wKHNvdXJjZSwgZnVuY3Rpb24gKHZhbHVlLCBwcm9wKSB7CiAgICAgICAgICAgICAgICBpZiAoZm9yY2UgfHwgIWhhc1Byb3AodGFyZ2V0LCBwcm9wKSkgewogICAgICAgICAgICAgICAgICAgIGlmIChkZWVwU3RyaW5nTWl4aW4gJiYgdHlwZW9mIHZhbHVlID09PSAnb2JqZWN0JyAmJiB2YWx1ZSAmJgogICAgICAgICAgICAgICAgICAgICAgICAhaXNBcnJheSh2YWx1ZSkgJiYgIWlzRnVuY3Rpb24odmFsdWUpICYmCiAgICAgICAgICAgICAgICAgICAgICAgICEodmFsdWUgaW5zdGFuY2VvZiBSZWdFeHApKSB7CgogICAgICAgICAgICAgICAgICAgICAgICBpZiAoIXRhcmdldFtwcm9wXSkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgdGFyZ2V0W3Byb3BdID0ge307CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgbWl4aW4odGFyZ2V0W3Byb3BdLCB2YWx1ZSwgZm9yY2UsIGRlZXBTdHJpbmdNaXhpbik7CiAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgICAgICAgICAgICAgdGFyZ2V0W3Byb3BdID0gdmFsdWU7CiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9KTsKICAgICAgICB9CiAgICAgICAgcmV0dXJuIHRhcmdldDsKICAgIH0KCiAgICAvL1NpbWlsYXIgdG8gRnVuY3Rpb24ucHJvdG90eXBlLmJpbmQsIGJ1dCB0aGUgJ3RoaXMnIG9iamVjdCBpcyBzcGVjaWZpZWQKICAgIC8vZmlyc3QsIHNpbmNlIGl0IGlzIGVhc2llciB0byByZWFkL2ZpZ3VyZSBvdXQgd2hhdCAndGhpcycgd2lsbCBiZS4KICAgIGZ1bmN0aW9uIGJpbmQob2JqLCBmbikgewogICAgICAgIHJldHVybiBmdW5jdGlvbiAoKSB7CiAgICAgICAgICAgIHJldHVybiBmbi5hcHBseShvYmosIGFyZ3VtZW50cyk7CiAgICAgICAgfTsKICAgIH0KCiAgICBmdW5jdGlvbiBzY3JpcHRzKCkgewogICAgICAgIHJldHVybiBkb2N1bWVudC5nZXRFbGVtZW50c0J5VGFnTmFtZSgnc2NyaXB0Jyk7CiAgICB9CgogICAgZnVuY3Rpb24gZGVmYXVsdE9uRXJyb3IoZXJyKSB7CiAgICAgICAgdGhyb3cgZXJyOwogICAgfQoKICAgIC8vQWxsb3cgZ2V0dGluZyBhIGdsb2JhbCB0aGF0IGlzIGV4cHJlc3NlZCBpbgogICAgLy9kb3Qgbm90YXRpb24sIGxpa2UgJ2EuYi5jJy4KICAgIGZ1bmN0aW9uIGdldEdsb2JhbCh2YWx1ZSkgewogICAgICAgIGlmICghdmFsdWUpIHsKICAgICAgICAgICAgcmV0dXJuIHZhbHVlOwogICAgICAgIH0KICAgICAgICB2YXIgZyA9IGdsb2JhbDsKICAgICAgICBlYWNoKHZhbHVlLnNwbGl0KCcuJyksIGZ1bmN0aW9uIChwYXJ0KSB7CiAgICAgICAgICAgIGcgPSBnW3BhcnRdOwogICAgICAgIH0pOwogICAgICAgIHJldHVybiBnOwogICAgfQoKICAgIC8qKgogICAgICogQ29uc3RydWN0cyBhbiBlcnJvciB3aXRoIGEgcG9pbnRlciB0byBhbiBVUkwgd2l0aCBtb3JlIGluZm9ybWF0aW9uLgogICAgICogQHBhcmFtIHtTdHJpbmd9IGlkIHRoZSBlcnJvciBJRCB0aGF0IG1hcHMgdG8gYW4gSUQgb24gYSB3ZWIgcGFnZS4KICAgICAqIEBwYXJhbSB7U3RyaW5nfSBtZXNzYWdlIGh1bWFuIHJlYWRhYmxlIGVycm9yLgogICAgICogQHBhcmFtIHtFcnJvcn0gW2Vycl0gdGhlIG9yaWdpbmFsIGVycm9yLCBpZiB0aGVyZSBpcyBvbmUuCiAgICAgKgogICAgICogQHJldHVybnMge0Vycm9yfQogICAgICovCiAgICBmdW5jdGlvbiBtYWtlRXJyb3IoaWQsIG1zZywgZXJyLCByZXF1aXJlTW9kdWxlcykgewogICAgICAgIHZhciBlID0gbmV3IEVycm9yKG1zZyArICdcbmh0dHA6Ly9yZXF1aXJlanMub3JnL2RvY3MvZXJyb3JzLmh0bWwjJyArIGlkKTsKICAgICAgICBlLnJlcXVpcmVUeXBlID0gaWQ7CiAgICAgICAgZS5yZXF1aXJlTW9kdWxlcyA9IHJlcXVpcmVNb2R1bGVzOwogICAgICAgIGlmIChlcnIpIHsKICAgICAgICAgICAgZS5vcmlnaW5hbEVycm9yID0gZXJyOwogICAgICAgIH0KICAgICAgICByZXR1cm4gZTsKICAgIH0KCiAgICBpZiAodHlwZW9mIGRlZmluZSAhPT0gJ3VuZGVmaW5lZCcpIHsKICAgICAgICAvL0lmIGEgZGVmaW5lIGlzIGFscmVhZHkgaW4gcGxheSB2aWEgYW5vdGhlciBBTUQgbG9hZGVyLAogICAgICAgIC8vZG8gbm90IG92ZXJ3cml0ZS4KICAgICAgICByZXR1cm47CiAgICB9CgogICAgaWYgKHR5cGVvZiByZXF1aXJlanMgIT09ICd1bmRlZmluZWQnKSB7CiAgICAgICAgaWYgKGlzRnVuY3Rpb24ocmVxdWlyZWpzKSkgewogICAgICAgICAgICAvL0RvIG5vdCBvdmVyd3JpdGUgYW4gZXhpc3RpbmcgcmVxdWlyZWpzIGluc3RhbmNlLgogICAgICAgICAgICByZXR1cm47CiAgICAgICAgfQogICAgICAgIGNmZyA9IHJlcXVpcmVqczsKICAgICAgICByZXF1aXJlanMgPSB1bmRlZmluZWQ7CiAgICB9CgogICAgLy9BbGxvdyBmb3IgYSByZXF1aXJlIGNvbmZpZyBvYmplY3QKICAgIGlmICh0eXBlb2YgcmVxdWlyZSAhPT0gJ3VuZGVmaW5lZCcgJiYgIWlzRnVuY3Rpb24ocmVxdWlyZSkpIHsKICAgICAgICAvL2Fzc3VtZSBpdCBpcyBhIGNvbmZpZyBvYmplY3QuCiAgICAgICAgY2ZnID0gcmVxdWlyZTsKICAgICAgICByZXF1aXJlID0gdW5kZWZpbmVkOwogICAgfQoKICAgIGZ1bmN0aW9uIG5ld0NvbnRleHQoY29udGV4dE5hbWUpIHsKICAgICAgICB2YXIgaW5DaGVja0xvYWRlZCwgTW9kdWxlLCBjb250ZXh0LCBoYW5kbGVycywKICAgICAgICAgICAgY2hlY2tMb2FkZWRUaW1lb3V0SWQsCiAgICAgICAgICAgIGNvbmZpZyA9IHsKICAgICAgICAgICAgICAgIC8vRGVmYXVsdHMuIERvIG5vdCBzZXQgYSBkZWZhdWx0IGZvciBtYXAKICAgICAgICAgICAgICAgIC8vY29uZmlnIHRvIHNwZWVkIHVwIG5vcm1hbGl6ZSgpLCB3aGljaAogICAgICAgICAgICAgICAgLy93aWxsIHJ1biBmYXN0ZXIgaWYgdGhlcmUgaXMgbm8gZGVmYXVsdC4KICAgICAgICAgICAgICAgIHdhaXRTZWNvbmRzOiA3LAogICAgICAgICAgICAgICAgYmFzZVVybDogJy4vJywKICAgICAgICAgICAgICAgIHBhdGhzOiB7fSwKICAgICAgICAgICAgICAgIGJ1bmRsZXM6IHt9LAogICAgICAgICAgICAgICAgcGtnczoge30sCiAgICAgICAgICAgICAgICBzaGltOiB7fSwKICAgICAgICAgICAgICAgIGNvbmZpZzoge30KICAgICAgICAgICAgfSwKICAgICAgICAgICAgcmVnaXN0cnkgPSB7fSwKICAgICAgICAgICAgLy9yZWdpc3RyeSBvZiBqdXN0IGVuYWJsZWQgbW9kdWxlcywgdG8gc3BlZWQKICAgICAgICAgICAgLy9jeWNsZSBicmVha2luZyBjb2RlIHdoZW4gbG90cyBvZiBtb2R1bGVzCiAgICAgICAgICAgIC8vYXJlIHJlZ2lzdGVyZWQsIGJ1dCBub3QgYWN0aXZhdGVkLgogICAgICAgICAgICBlbmFibGVkUmVnaXN0cnkgPSB7fSwKICAgICAgICAgICAgdW5kZWZFdmVudHMgPSB7fSwKICAgICAgICAgICAgZGVmUXVldWUgPSBbXSwKICAgICAgICAgICAgZGVmaW5lZCA9IHt9LAogICAgICAgICAgICB1cmxGZXRjaGVkID0ge30sCiAgICAgICAgICAgIGJ1bmRsZXNNYXAgPSB7fSwKICAgICAgICAgICAgcmVxdWlyZUNvdW50ZXIgPSAxLAogICAgICAgICAgICB1bm5vcm1hbGl6ZWRDb3VudGVyID0gMTsKCiAgICAgICAgLyoqCiAgICAgICAgICogVHJpbXMgdGhlIC4gYW5kIC4uIGZyb20gYW4gYXJyYXkgb2YgcGF0aCBzZWdtZW50cy4KICAgICAgICAgKiBJdCB3aWxsIGtlZXAgYSBsZWFkaW5nIHBhdGggc2VnbWVudCBpZiBhIC4uIHdpbGwgYmVjb21lCiAgICAgICAgICogdGhlIGZpcnN0IHBhdGggc2VnbWVudCwgdG8gaGVscCB3aXRoIG1vZHVsZSBuYW1lIGxvb2t1cHMsCiAgICAgICAgICogd2hpY2ggYWN0IGxpa2UgcGF0aHMsIGJ1dCBjYW4gYmUgcmVtYXBwZWQuIEJ1dCB0aGUgZW5kIHJlc3VsdCwKICAgICAgICAgKiBhbGwgcGF0aHMgdGhhdCB1c2UgdGhpcyBmdW5jdGlvbiBzaG91bGQgbG9vayBub3JtYWxpemVkLgogICAgICAgICAqIE5PVEU6IHRoaXMgbWV0aG9kIE1PRElGSUVTIHRoZSBpbnB1dCBhcnJheS4KICAgICAgICAgKiBAcGFyYW0ge0FycmF5fSBhcnkgdGhlIGFycmF5IG9mIHBhdGggc2VnbWVudHMuCiAgICAgICAgICovCiAgICAgICAgZnVuY3Rpb24gdHJpbURvdHMoYXJ5KSB7CiAgICAgICAgICAgIHZhciBpLCBwYXJ0OwogICAgICAgICAgICBmb3IgKGkgPSAwOyBpIDwgYXJ5Lmxlbmd0aDsgaSsrKSB7CiAgICAgICAgICAgICAgICBwYXJ0ID0gYXJ5W2ldOwogICAgICAgICAgICAgICAgaWYgKHBhcnQgPT09ICcuJykgewogICAgICAgICAgICAgICAgICAgIGFyeS5zcGxpY2UoaSwgMSk7CiAgICAgICAgICAgICAgICAgICAgaSAtPSAxOwogICAgICAgICAgICAgICAgfSBlbHNlIGlmIChwYXJ0ID09PSAnLi4nKSB7CiAgICAgICAgICAgICAgICAgICAgLy8gSWYgYXQgdGhlIHN0YXJ0LCBvciBwcmV2aW91cyB2YWx1ZSBpcyBzdGlsbCAuLiwKICAgICAgICAgICAgICAgICAgICAvLyBrZWVwIHRoZW0gc28gdGhhdCB3aGVuIGNvbnZlcnRlZCB0byBhIHBhdGggaXQgbWF5CiAgICAgICAgICAgICAgICAgICAgLy8gc3RpbGwgd29yayB3aGVuIGNvbnZlcnRlZCB0byBhIHBhdGgsIGV2ZW4gdGhvdWdoCiAgICAgICAgICAgICAgICAgICAgLy8gYXMgYW4gSUQgaXQgaXMgbGVzcyB0aGFuIGlkZWFsLiBJbiBsYXJnZXIgcG9pbnQKICAgICAgICAgICAgICAgICAgICAvLyByZWxlYXNlcywgbWF5IGJlIGJldHRlciB0byBqdXN0IGtpY2sgb3V0IGFuIGVycm9yLgogICAgICAgICAgICAgICAgICAgIGlmIChpID09PSAwIHx8IChpID09PSAxICYmIGFyeVsyXSA9PT0gJy4uJykgfHwgYXJ5W2kgLSAxXSA9PT0gJy4uJykgewogICAgICAgICAgICAgICAgICAgICAgICBjb250aW51ZTsKICAgICAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKGkgPiAwKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIGFyeS5zcGxpY2UoaSAtIDEsIDIpOwogICAgICAgICAgICAgICAgICAgICAgICBpIC09IDI7CiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CiAgICAgICAgfQoKICAgICAgICAvKioKICAgICAgICAgKiBHaXZlbiBhIHJlbGF0aXZlIG1vZHVsZSBuYW1lLCBsaWtlIC4vc29tZXRoaW5nLCBub3JtYWxpemUgaXQgdG8KICAgICAgICAgKiBhIHJlYWwgbmFtZSB0aGF0IGNhbiBiZSBtYXBwZWQgdG8gYSBwYXRoLgogICAgICAgICAqIEBwYXJhbSB7U3RyaW5nfSBuYW1lIHRoZSByZWxhdGl2ZSBuYW1lCiAgICAgICAgICogQHBhcmFtIHtTdHJpbmd9IGJhc2VOYW1lIGEgcmVhbCBuYW1lIHRoYXQgdGhlIG5hbWUgYXJnIGlzIHJlbGF0aXZlCiAgICAgICAgICogdG8uCiAgICAgICAgICogQHBhcmFtIHtCb29sZWFufSBhcHBseU1hcCBhcHBseSB0aGUgbWFwIGNvbmZpZyB0byB0aGUgdmFsdWUuIFNob3VsZAogICAgICAgICAqIG9ubHkgYmUgZG9uZSBpZiB0aGlzIG5vcm1hbGl6YXRpb24gaXMgZm9yIGEgZGVwZW5kZW5jeSBJRC4KICAgICAgICAgKiBAcmV0dXJucyB7U3RyaW5nfSBub3JtYWxpemVkIG5hbWUKICAgICAgICAgKi8KICAgICAgICBmdW5jdGlvbiBub3JtYWxpemUobmFtZSwgYmFzZU5hbWUsIGFwcGx5TWFwKSB7CiAgICAgICAgICAgIHZhciBwa2dNYWluLCBtYXBWYWx1ZSwgbmFtZVBhcnRzLCBpLCBqLCBuYW1lU2VnbWVudCwgbGFzdEluZGV4LAogICAgICAgICAgICAgICAgZm91bmRNYXAsIGZvdW5kSSwgZm91bmRTdGFyTWFwLCBzdGFySSwgbm9ybWFsaXplZEJhc2VQYXJ0cywKICAgICAgICAgICAgICAgIGJhc2VQYXJ0cyA9IChiYXNlTmFtZSAmJiBiYXNlTmFtZS5zcGxpdCgnLycpKSwKICAgICAgICAgICAgICAgIG1hcCA9IGNvbmZpZy5tYXAsCiAgICAgICAgICAgICAgICBzdGFyTWFwID0gbWFwICYmIG1hcFsnKiddOwoKICAgICAgICAgICAgLy9BZGp1c3QgYW55IHJlbGF0aXZlIHBhdGhzLgogICAgICAgICAgICBpZiAobmFtZSkgewogICAgICAgICAgICAgICAgbmFtZSA9IG5hbWUuc3BsaXQoJy8nKTsKICAgICAgICAgICAgICAgIGxhc3RJbmRleCA9IG5hbWUubGVuZ3RoIC0gMTsKCiAgICAgICAgICAgICAgICAvLyBJZiB3YW50aW5nIG5vZGUgSUQgY29tcGF0aWJpbGl0eSwgc3RyaXAgLmpzIGZyb20gZW5kCiAgICAgICAgICAgICAgICAvLyBvZiBJRHMuIEhhdmUgdG8gZG8gdGhpcyBoZXJlLCBhbmQgbm90IGluIG5hbWVUb1VybAogICAgICAgICAgICAgICAgLy8gYmVjYXVzZSBub2RlIGFsbG93cyBlaXRoZXIgLmpzIG9yIG5vbiAuanMgdG8gbWFwCiAgICAgICAgICAgICAgICAvLyB0byBzYW1lIGZpbGUuCiAgICAgICAgICAgICAgICBpZiAoY29uZmlnLm5vZGVJZENvbXBhdCAmJiBqc1N1ZmZpeFJlZ0V4cC50ZXN0KG5hbWVbbGFzdEluZGV4XSkpIHsKICAgICAgICAgICAgICAgICAgICBuYW1lW2xhc3RJbmRleF0gPSBuYW1lW2xhc3RJbmRleF0ucmVwbGFjZShqc1N1ZmZpeFJlZ0V4cCwgJycpOwogICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgIC8vIFN0YXJ0cyB3aXRoIGEgJy4nIHNvIG5lZWQgdGhlIGJhc2VOYW1lCiAgICAgICAgICAgICAgICBpZiAobmFtZVswXS5jaGFyQXQoMCkgPT09ICcuJyAmJiBiYXNlUGFydHMpIHsKICAgICAgICAgICAgICAgICAgICAvL0NvbnZlcnQgYmFzZU5hbWUgdG8gYXJyYXksIGFuZCBsb3Agb2ZmIHRoZSBsYXN0IHBhcnQsCiAgICAgICAgICAgICAgICAgICAgLy9zbyB0aGF0IC4gbWF0Y2hlcyB0aGF0ICdkaXJlY3RvcnknIGFuZCBub3QgbmFtZSBvZiB0aGUgYmFzZU5hbWUncwogICAgICAgICAgICAgICAgICAgIC8vbW9kdWxlLiBGb3IgaW5zdGFuY2UsIGJhc2VOYW1lIG9mICdvbmUvdHdvL3RocmVlJywgbWFwcyB0bwogICAgICAgICAgICAgICAgICAgIC8vJ29uZS90d28vdGhyZWUuanMnLCBidXQgd2Ugd2FudCB0aGUgZGlyZWN0b3J5LCAnb25lL3R3bycgZm9yCiAgICAgICAgICAgICAgICAgICAgLy90aGlzIG5vcm1hbGl6YXRpb24uCiAgICAgICAgICAgICAgICAgICAgbm9ybWFsaXplZEJhc2VQYXJ0cyA9IGJhc2VQYXJ0cy5zbGljZSgwLCBiYXNlUGFydHMubGVuZ3RoIC0gMSk7CiAgICAgICAgICAgICAgICAgICAgbmFtZSA9IG5vcm1hbGl6ZWRCYXNlUGFydHMuY29uY2F0KG5hbWUpOwogICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgIHRyaW1Eb3RzKG5hbWUpOwogICAgICAgICAgICAgICAgbmFtZSA9IG5hbWUuam9pbignLycpOwogICAgICAgICAgICB9CgogICAgICAgICAgICAvL0FwcGx5IG1hcCBjb25maWcgaWYgYXZhaWxhYmxlLgogICAgICAgICAgICBpZiAoYXBwbHlNYXAgJiYgbWFwICYmIChiYXNlUGFydHMgfHwgc3Rhck1hcCkpIHsKICAgICAgICAgICAgICAgIG5hbWVQYXJ0cyA9IG5hbWUuc3BsaXQoJy8nKTsKCiAgICAgICAgICAgICAgICBvdXRlckxvb3A6IGZvciAoaSA9IG5hbWVQYXJ0cy5sZW5ndGg7IGkgPiAwOyBpIC09IDEpIHsKICAgICAgICAgICAgICAgICAgICBuYW1lU2VnbWVudCA9IG5hbWVQYXJ0cy5zbGljZSgwLCBpKS5qb2luKCcvJyk7CgogICAgICAgICAgICAgICAgICAgIGlmIChiYXNlUGFydHMpIHsKICAgICAgICAgICAgICAgICAgICAgICAgLy9GaW5kIHRoZSBsb25nZXN0IGJhc2VOYW1lIHNlZ21lbnQgbWF0Y2ggaW4gdGhlIGNvbmZpZy4KICAgICAgICAgICAgICAgICAgICAgICAgLy9TbywgZG8gam9pbnMgb24gdGhlIGJpZ2dlc3QgdG8gc21hbGxlc3QgbGVuZ3RocyBvZiBiYXNlUGFydHMuCiAgICAgICAgICAgICAgICAgICAgICAgIGZvciAoaiA9IGJhc2VQYXJ0cy5sZW5ndGg7IGogPiAwOyBqIC09IDEpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1hcFZhbHVlID0gZ2V0T3duKG1hcCwgYmFzZVBhcnRzLnNsaWNlKDAsIGopLmpvaW4oJy8nKSk7CgogICAgICAgICAgICAgICAgICAgICAgICAgICAgLy9iYXNlTmFtZSBzZWdtZW50IGhhcyBjb25maWcsIGZpbmQgaWYgaXQgaGFzIG9uZSBmb3IKICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vdGhpcyBuYW1lLgogICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKG1hcFZhbHVlKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWFwVmFsdWUgPSBnZXRPd24obWFwVmFsdWUsIG5hbWVTZWdtZW50KTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAobWFwVmFsdWUpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy9NYXRjaCwgdXBkYXRlIG5hbWUgdG8gdGhlIG5ldyB2YWx1ZS4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZm91bmRNYXAgPSBtYXBWYWx1ZTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZm91bmRJID0gaTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWsgb3V0ZXJMb29wOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICAgICAgLy9DaGVjayBmb3IgYSBzdGFyIG1hcCBtYXRjaCwgYnV0IGp1c3QgaG9sZCBvbiB0byBpdCwKICAgICAgICAgICAgICAgICAgICAvL2lmIHRoZXJlIGlzIGEgc2hvcnRlciBzZWdtZW50IG1hdGNoIGxhdGVyIGluIGEgbWF0Y2hpbmcKICAgICAgICAgICAgICAgICAgICAvL2NvbmZpZywgdGhlbiBmYXZvciBvdmVyIHRoaXMgc3RhciBtYXAuCiAgICAgICAgICAgICAgICAgICAgaWYgKCFmb3VuZFN0YXJNYXAgJiYgc3Rhck1hcCAmJiBnZXRPd24oc3Rhck1hcCwgbmFtZVNlZ21lbnQpKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIGZvdW5kU3Rhck1hcCA9IGdldE93bihzdGFyTWFwLCBuYW1lU2VnbWVudCk7CiAgICAgICAgICAgICAgICAgICAgICAgIHN0YXJJID0gaTsKICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgaWYgKCFmb3VuZE1hcCAmJiBmb3VuZFN0YXJNYXApIHsKICAgICAgICAgICAgICAgICAgICBmb3VuZE1hcCA9IGZvdW5kU3Rhck1hcDsKICAgICAgICAgICAgICAgICAgICBmb3VuZEkgPSBzdGFySTsKICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICBpZiAoZm91bmRNYXApIHsKICAgICAgICAgICAgICAgICAgICBuYW1lUGFydHMuc3BsaWNlKDAsIGZvdW5kSSwgZm91bmRNYXApOwogICAgICAgICAgICAgICAgICAgIG5hbWUgPSBuYW1lUGFydHMuam9pbignLycpOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CgogICAgICAgICAgICAvLyBJZiB0aGUgbmFtZSBwb2ludHMgdG8gYSBwYWNrYWdlJ3MgbmFtZSwgdXNlCiAgICAgICAgICAgIC8vIHRoZSBwYWNrYWdlIG1haW4gaW5zdGVhZC4KICAgICAgICAgICAgcGtnTWFpbiA9IGdldE93bihjb25maWcucGtncywgbmFtZSk7CgogICAgICAgICAgICByZXR1cm4gcGtnTWFpbiA/IHBrZ01haW4gOiBuYW1lOwogICAgICAgIH0KCiAgICAgICAgZnVuY3Rpb24gcmVtb3ZlU2NyaXB0KG5hbWUpIHsKICAgICAgICAgICAgaWYgKGlzQnJvd3NlcikgewogICAgICAgICAgICAgICAgZWFjaChzY3JpcHRzKCksIGZ1bmN0aW9uIChzY3JpcHROb2RlKSB7CiAgICAgICAgICAgICAgICAgICAgaWYgKHNjcmlwdE5vZGUuZ2V0QXR0cmlidXRlKCdkYXRhLXJlcXVpcmVtb2R1bGUnKSA9PT0gbmFtZSAmJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgc2NyaXB0Tm9kZS5nZXRBdHRyaWJ1dGUoJ2RhdGEtcmVxdWlyZWNvbnRleHQnKSA9PT0gY29udGV4dC5jb250ZXh0TmFtZSkgewogICAgICAgICAgICAgICAgICAgICAgICBzY3JpcHROb2RlLnBhcmVudE5vZGUucmVtb3ZlQ2hpbGQoc2NyaXB0Tm9kZSk7CiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiB0cnVlOwogICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIH0pOwogICAgICAgICAgICB9CiAgICAgICAgfQoKICAgICAgICBmdW5jdGlvbiBoYXNQYXRoRmFsbGJhY2soaWQpIHsKICAgICAgICAgICAgdmFyIHBhdGhDb25maWcgPSBnZXRPd24oY29uZmlnLnBhdGhzLCBpZCk7CiAgICAgICAgICAgIGlmIChwYXRoQ29uZmlnICYmIGlzQXJyYXkocGF0aENvbmZpZykgJiYgcGF0aENvbmZpZy5sZW5ndGggPiAxKSB7CiAgICAgICAgICAgICAgICAvL1BvcCBvZmYgdGhlIGZpcnN0IGFycmF5IHZhbHVlLCBzaW5jZSBpdCBmYWlsZWQsIGFuZAogICAgICAgICAgICAgICAgLy9yZXRyeQogICAgICAgICAgICAgICAgcGF0aENvbmZpZy5zaGlmdCgpOwogICAgICAgICAgICAgICAgY29udGV4dC5yZXF1aXJlLnVuZGVmKGlkKTsKCiAgICAgICAgICAgICAgICAvL0N1c3RvbSByZXF1aXJlIHRoYXQgZG9lcyBub3QgZG8gbWFwIHRyYW5zbGF0aW9uLCBzaW5jZQogICAgICAgICAgICAgICAgLy9JRCBpcyAiYWJzb2x1dGUiLCBhbHJlYWR5IG1hcHBlZC9yZXNvbHZlZC4KICAgICAgICAgICAgICAgIGNvbnRleHQubWFrZVJlcXVpcmUobnVsbCwgewogICAgICAgICAgICAgICAgICAgIHNraXBNYXA6IHRydWUKICAgICAgICAgICAgICAgIH0pKFtpZF0pOwoKICAgICAgICAgICAgICAgIHJldHVybiB0cnVlOwogICAgICAgICAgICB9CiAgICAgICAgfQoKICAgICAgICAvL1R1cm5zIGEgcGx1Z2luIXJlc291cmNlIHRvIFtwbHVnaW4sIHJlc291cmNlXQogICAgICAgIC8vd2l0aCB0aGUgcGx1Z2luIGJlaW5nIHVuZGVmaW5lZCBpZiB0aGUgbmFtZQogICAgICAgIC8vZGlkIG5vdCBoYXZlIGEgcGx1Z2luIHByZWZpeC4KICAgICAgICBmdW5jdGlvbiBzcGxpdFByZWZpeChuYW1lKSB7CiAgICAgICAgICAgIHZhciBwcmVmaXgsCiAgICAgICAgICAgICAgICBpbmRleCA9IG5hbWUgPyBuYW1lLmluZGV4T2YoJyEnKSA6IC0xOwogICAgICAgICAgICBpZiAoaW5kZXggPiAtMSkgewogICAgICAgICAgICAgICAgcHJlZml4ID0gbmFtZS5zdWJzdHJpbmcoMCwgaW5kZXgpOwogICAgICAgICAgICAgICAgbmFtZSA9IG5hbWUuc3Vic3RyaW5nKGluZGV4ICsgMSwgbmFtZS5sZW5ndGgpOwogICAgICAgICAgICB9CiAgICAgICAgICAgIHJldHVybiBbcHJlZml4LCBuYW1lXTsKICAgICAgICB9CgogICAgICAgIC8qKgogICAgICAgICAqIENyZWF0ZXMgYSBtb2R1bGUgbWFwcGluZyB0aGF0IGluY2x1ZGVzIHBsdWdpbiBwcmVmaXgsIG1vZHVsZQogICAgICAgICAqIG5hbWUsIGFuZCBwYXRoLiBJZiBwYXJlbnRNb2R1bGVNYXAgaXMgcHJvdmlkZWQgaXQgd2lsbAogICAgICAgICAqIGFsc28gbm9ybWFsaXplIHRoZSBuYW1lIHZpYSByZXF1aXJlLm5vcm1hbGl6ZSgpCiAgICAgICAgICoKICAgICAgICAgKiBAcGFyYW0ge1N0cmluZ30gbmFtZSB0aGUgbW9kdWxlIG5hbWUKICAgICAgICAgKiBAcGFyYW0ge1N0cmluZ30gW3BhcmVudE1vZHVsZU1hcF0gcGFyZW50IG1vZHVsZSBtYXAKICAgICAgICAgKiBmb3IgdGhlIG1vZHVsZSBuYW1lLCB1c2VkIHRvIHJlc29sdmUgcmVsYXRpdmUgbmFtZXMuCiAgICAgICAgICogQHBhcmFtIHtCb29sZWFufSBpc05vcm1hbGl6ZWQ6IGlzIHRoZSBJRCBhbHJlYWR5IG5vcm1hbGl6ZWQuCiAgICAgICAgICogVGhpcyBpcyB0cnVlIGlmIHRoaXMgY2FsbCBpcyBkb25lIGZvciBhIGRlZmluZSgpIG1vZHVsZSBJRC4KICAgICAgICAgKiBAcGFyYW0ge0Jvb2xlYW59IGFwcGx5TWFwOiBhcHBseSB0aGUgbWFwIGNvbmZpZyB0byB0aGUgSUQuCiAgICAgICAgICogU2hvdWxkIG9ubHkgYmUgdHJ1ZSBpZiB0aGlzIG1hcCBpcyBmb3IgYSBkZXBlbmRlbmN5LgogICAgICAgICAqCiAgICAgICAgICogQHJldHVybnMge09iamVjdH0KICAgICAgICAgKi8KICAgICAgICBmdW5jdGlvbiBtYWtlTW9kdWxlTWFwKG5hbWUsIHBhcmVudE1vZHVsZU1hcCwgaXNOb3JtYWxpemVkLCBhcHBseU1hcCkgewogICAgICAgICAgICB2YXIgdXJsLCBwbHVnaW5Nb2R1bGUsIHN1ZmZpeCwgbmFtZVBhcnRzLAogICAgICAgICAgICAgICAgcHJlZml4ID0gbnVsbCwKICAgICAgICAgICAgICAgIHBhcmVudE5hbWUgPSBwYXJlbnRNb2R1bGVNYXAgPyBwYXJlbnRNb2R1bGVNYXAubmFtZSA6IG51bGwsCiAgICAgICAgICAgICAgICBvcmlnaW5hbE5hbWUgPSBuYW1lLAogICAgICAgICAgICAgICAgaXNEZWZpbmUgPSB0cnVlLAogICAgICAgICAgICAgICAgbm9ybWFsaXplZE5hbWUgPSAnJzsKCiAgICAgICAgICAgIC8vSWYgbm8gbmFtZSwgdGhlbiBpdCBtZWFucyBpdCBpcyBhIHJlcXVpcmUgY2FsbCwgZ2VuZXJhdGUgYW4KICAgICAgICAgICAgLy9pbnRlcm5hbCBuYW1lLgogICAgICAgICAgICBpZiAoIW5hbWUpIHsKICAgICAgICAgICAgICAgIGlzRGVmaW5lID0gZmFsc2U7CiAgICAgICAgICAgICAgICBuYW1lID0gJ19AcicgKyAocmVxdWlyZUNvdW50ZXIgKz0gMSk7CiAgICAgICAgICAgIH0KCiAgICAgICAgICAgIG5hbWVQYXJ0cyA9IHNwbGl0UHJlZml4KG5hbWUpOwogICAgICAgICAgICBwcmVmaXggPSBuYW1lUGFydHNbMF07CiAgICAgICAgICAgIG5hbWUgPSBuYW1lUGFydHNbMV07CgogICAgICAgICAgICBpZiAocHJlZml4KSB7CiAgICAgICAgICAgICAgICBwcmVmaXggPSBub3JtYWxpemUocHJlZml4LCBwYXJlbnROYW1lLCBhcHBseU1hcCk7CiAgICAgICAgICAgICAgICBwbHVnaW5Nb2R1bGUgPSBnZXRPd24oZGVmaW5lZCwgcHJlZml4KTsKICAgICAgICAgICAgfQoKICAgICAgICAgICAgLy9BY2NvdW50IGZvciByZWxhdGl2ZSBwYXRocyBpZiB0aGVyZSBpcyBhIGJhc2UgbmFtZS4KICAgICAgICAgICAgaWYgKG5hbWUpIHsKICAgICAgICAgICAgICAgIGlmIChwcmVmaXgpIHsKICAgICAgICAgICAgICAgICAgICBpZiAocGx1Z2luTW9kdWxlICYmIHBsdWdpbk1vZHVsZS5ub3JtYWxpemUpIHsKICAgICAgICAgICAgICAgICAgICAgICAgLy9QbHVnaW4gaXMgbG9hZGVkLCB1c2UgaXRzIG5vcm1hbGl6ZSBtZXRob2QuCiAgICAgICAgICAgICAgICAgICAgICAgIG5vcm1hbGl6ZWROYW1lID0gcGx1Z2luTW9kdWxlLm5vcm1hbGl6ZShuYW1lLCBmdW5jdGlvbiAobmFtZSkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIG5vcm1hbGl6ZShuYW1lLCBwYXJlbnROYW1lLCBhcHBseU1hcCk7CiAgICAgICAgICAgICAgICAgICAgICAgIH0pOwogICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgICAgICAgICAgICAgIC8vIElmIG5lc3RlZCBwbHVnaW4gcmVmZXJlbmNlcywgdGhlbiBkbyBub3QgdHJ5IHRvCiAgICAgICAgICAgICAgICAgICAgICAgIC8vIG5vcm1hbGl6ZSwgYXMgaXQgd2lsbCBub3Qgbm9ybWFsaXplIGNvcnJlY3RseS4gVGhpcwogICAgICAgICAgICAgICAgICAgICAgICAvLyBwbGFjZXMgYSByZXN0cmljdGlvbiBvbiByZXNvdXJjZUlkcywgYW5kIHRoZSBsb25nZXIKICAgICAgICAgICAgICAgICAgICAgICAgLy8gdGVybSBzb2x1dGlvbiBpcyBub3QgdG8gbm9ybWFsaXplIHVudGlsIHBsdWdpbnMgYXJlCiAgICAgICAgICAgICAgICAgICAgICAgIC8vIGxvYWRlZCBhbmQgYWxsIG5vcm1hbGl6YXRpb25zIHRvIGFsbG93IGZvciBhc3luYwogICAgICAgICAgICAgICAgICAgICAgICAvLyBsb2FkaW5nIG9mIGEgbG9hZGVyIHBsdWdpbi4gQnV0IGZvciBub3csIGZpeGVzIHRoZQogICAgICAgICAgICAgICAgICAgICAgICAvLyBjb21tb24gdXNlcy4gRGV0YWlscyBpbiAjMTEzMQogICAgICAgICAgICAgICAgICAgICAgICBub3JtYWxpemVkTmFtZSA9IG5hbWUuaW5kZXhPZignIScpID09PSAtMSA/CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm9ybWFsaXplKG5hbWUsIHBhcmVudE5hbWUsIGFwcGx5TWFwKSA6CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmFtZTsKICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICAgICAgICAgIC8vQSByZWd1bGFyIG1vZHVsZS4KICAgICAgICAgICAgICAgICAgICBub3JtYWxpemVkTmFtZSA9IG5vcm1hbGl6ZShuYW1lLCBwYXJlbnROYW1lLCBhcHBseU1hcCk7CgogICAgICAgICAgICAgICAgICAgIC8vTm9ybWFsaXplZCBuYW1lIG1heSBiZSBhIHBsdWdpbiBJRCBkdWUgdG8gbWFwIGNvbmZpZwogICAgICAgICAgICAgICAgICAgIC8vYXBwbGljYXRpb24gaW4gbm9ybWFsaXplLiBUaGUgbWFwIGNvbmZpZyB2YWx1ZXMgbXVzdAogICAgICAgICAgICAgICAgICAgIC8vYWxyZWFkeSBiZSBub3JtYWxpemVkLCBzbyBkbyBub3QgbmVlZCB0byByZWRvIHRoYXQgcGFydC4KICAgICAgICAgICAgICAgICAgICBuYW1lUGFydHMgPSBzcGxpdFByZWZpeChub3JtYWxpemVkTmFtZSk7CiAgICAgICAgICAgICAgICAgICAgcHJlZml4ID0gbmFtZVBhcnRzWzBdOwogICAgICAgICAgICAgICAgICAgIG5vcm1hbGl6ZWROYW1lID0gbmFtZVBhcnRzWzFdOwogICAgICAgICAgICAgICAgICAgIGlzTm9ybWFsaXplZCA9IHRydWU7CgogICAgICAgICAgICAgICAgICAgIHVybCA9IGNvbnRleHQubmFtZVRvVXJsKG5vcm1hbGl6ZWROYW1lKTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQoKICAgICAgICAgICAgLy9JZiB0aGUgaWQgaXMgYSBwbHVnaW4gaWQgdGhhdCBjYW5ub3QgYmUgZGV0ZXJtaW5lZCBpZiBpdCBuZWVkcwogICAgICAgICAgICAvL25vcm1hbGl6YXRpb24sIHN0YW1wIGl0IHdpdGggYSB1bmlxdWUgSUQgc28gdHdvIG1hdGNoaW5nIHJlbGF0aXZlCiAgICAgICAgICAgIC8vaWRzIHRoYXQgbWF5IGNvbmZsaWN0IGNhbiBiZSBzZXBhcmF0ZS4KICAgICAgICAgICAgc3VmZml4ID0gcHJlZml4ICYmICFwbHVnaW5Nb2R1bGUgJiYgIWlzTm9ybWFsaXplZCA/CiAgICAgICAgICAgICAgICAgICAgICdfdW5ub3JtYWxpemVkJyArICh1bm5vcm1hbGl6ZWRDb3VudGVyICs9IDEpIDoKICAgICAgICAgICAgICAgICAgICAgJyc7CgogICAgICAgICAgICByZXR1cm4gewogICAgICAgICAgICAgICAgcHJlZml4OiBwcmVmaXgsCiAgICAgICAgICAgICAgICBuYW1lOiBub3JtYWxpemVkTmFtZSwKICAgICAgICAgICAgICAgIHBhcmVudE1hcDogcGFyZW50TW9kdWxlTWFwLAogICAgICAgICAgICAgICAgdW5ub3JtYWxpemVkOiAhIXN1ZmZpeCwKICAgICAgICAgICAgICAgIHVybDogdXJsLAogICAgICAgICAgICAgICAgb3JpZ2luYWxOYW1lOiBvcmlnaW5hbE5hbWUsCiAgICAgICAgICAgICAgICBpc0RlZmluZTogaXNEZWZpbmUsCiAgICAgICAgICAgICAgICBpZDogKHByZWZpeCA/CiAgICAgICAgICAgICAgICAgICAgICAgIHByZWZpeCArICchJyArIG5vcm1hbGl6ZWROYW1lIDoKICAgICAgICAgICAgICAgICAgICAgICAgbm9ybWFsaXplZE5hbWUpICsgc3VmZml4CiAgICAgICAgICAgIH07CiAgICAgICAgfQoKICAgICAgICBmdW5jdGlvbiBnZXRNb2R1bGUoZGVwTWFwKSB7CiAgICAgICAgICAgIHZhciBpZCA9IGRlcE1hcC5pZCwKICAgICAgICAgICAgICAgIG1vZCA9IGdldE93bihyZWdpc3RyeSwgaWQpOwoKICAgICAgICAgICAgaWYgKCFtb2QpIHsKICAgICAgICAgICAgICAgIG1vZCA9IHJlZ2lzdHJ5W2lkXSA9IG5ldyBjb250ZXh0Lk1vZHVsZShkZXBNYXApOwogICAgICAgICAgICB9CgogICAgICAgICAgICByZXR1cm4gbW9kOwogICAgICAgIH0KCiAgICAgICAgZnVuY3Rpb24gb24oZGVwTWFwLCBuYW1lLCBmbikgewogICAgICAgICAgICB2YXIgaWQgPSBkZXBNYXAuaWQsCiAgICAgICAgICAgICAgICBtb2QgPSBnZXRPd24ocmVnaXN0cnksIGlkKTsKCiAgICAgICAgICAgIGlmIChoYXNQcm9wKGRlZmluZWQsIGlkKSAmJgogICAgICAgICAgICAgICAgICAgICghbW9kIHx8IG1vZC5kZWZpbmVFbWl0Q29tcGxldGUpKSB7CiAgICAgICAgICAgICAgICBpZiAobmFtZSA9PT0gJ2RlZmluZWQnKSB7CiAgICAgICAgICAgICAgICAgICAgZm4oZGVmaW5lZFtpZF0pOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICAgICAgbW9kID0gZ2V0TW9kdWxlKGRlcE1hcCk7CiAgICAgICAgICAgICAgICBpZiAobW9kLmVycm9yICYmIG5hbWUgPT09ICdlcnJvcicpIHsKICAgICAgICAgICAgICAgICAgICBmbihtb2QuZXJyb3IpOwogICAgICAgICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgICAgICAgICBtb2Qub24obmFtZSwgZm4pOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CiAgICAgICAgfQoKICAgICAgICBmdW5jdGlvbiBvbkVycm9yKGVyciwgZXJyYmFjaykgewogICAgICAgICAgICB2YXIgaWRzID0gZXJyLnJlcXVpcmVNb2R1bGVzLAogICAgICAgICAgICAgICAgbm90aWZpZWQgPSBmYWxzZTsKCiAgICAgICAgICAgIGlmIChlcnJiYWNrKSB7CiAgICAgICAgICAgICAgICBlcnJiYWNrKGVycik7CiAgICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgICAgICBlYWNoKGlkcywgZnVuY3Rpb24gKGlkKSB7CiAgICAgICAgICAgICAgICAgICAgdmFyIG1vZCA9IGdldE93bihyZWdpc3RyeSwgaWQpOwogICAgICAgICAgICAgICAgICAgIGlmIChtb2QpIHsKICAgICAgICAgICAgICAgICAgICAgICAgLy9TZXQgZXJyb3Igb24gbW9kdWxlLCBzbyBpdCBza2lwcyB0aW1lb3V0IGNoZWNrcy4KICAgICAgICAgICAgICAgICAgICAgICAgbW9kLmVycm9yID0gZXJyOwogICAgICAgICAgICAgICAgICAgICAgICBpZiAobW9kLmV2ZW50cy5lcnJvcikgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgbm90aWZpZWQgPSB0cnVlOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9kLmVtaXQoJ2Vycm9yJywgZXJyKTsKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIH0pOwoKICAgICAgICAgICAgICAgIGlmICghbm90aWZpZWQpIHsKICAgICAgICAgICAgICAgICAgICByZXEub25FcnJvcihlcnIpOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CiAgICAgICAgfQoKICAgICAgICAvKioKICAgICAgICAgKiBJbnRlcm5hbCBtZXRob2QgdG8gdHJhbnNmZXIgZ2xvYmFsUXVldWUgaXRlbXMgdG8gdGhpcyBjb250ZXh0J3MKICAgICAgICAgKiBkZWZRdWV1ZS4KICAgICAgICAgKi8KICAgICAgICBmdW5jdGlvbiB0YWtlR2xvYmFsUXVldWUoKSB7CiAgICAgICAgICAgIC8vUHVzaCBhbGwgdGhlIGdsb2JhbERlZlF1ZXVlIGl0ZW1zIGludG8gdGhlIGNvbnRleHQncyBkZWZRdWV1ZQogICAgICAgICAgICBpZiAoZ2xvYmFsRGVmUXVldWUubGVuZ3RoKSB7CiAgICAgICAgICAgICAgICBlYWNoKGdsb2JhbERlZlF1ZXVlLCBmdW5jdGlvbihxdWV1ZUl0ZW0pIHsKICAgICAgICAgICAgICAgICAgICB2YXIgaWQgPSBxdWV1ZUl0ZW1bMF07CiAgICAgICAgICAgICAgICAgICAgaWYgKHR5cGVvZiBpZCA9PT0gJ3N0cmluZycpIHsKICAgICAgICAgICAgICAgICAgICAgICAgY29udGV4dC5kZWZRdWV1ZU1hcFtpZF0gPSB0cnVlOwogICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICBkZWZRdWV1ZS5wdXNoKHF1ZXVlSXRlbSk7CiAgICAgICAgICAgICAgICB9KTsKICAgICAgICAgICAgICAgIGdsb2JhbERlZlF1ZXVlID0gW107CiAgICAgICAgICAgIH0KICAgICAgICB9CgogICAgICAgIGhhbmRsZXJzID0gewogICAgICAgICAgICAncmVxdWlyZSc6IGZ1bmN0aW9uIChtb2QpIHsKICAgICAgICAgICAgICAgIGlmIChtb2QucmVxdWlyZSkgewogICAgICAgICAgICAgICAgICAgIHJldHVybiBtb2QucmVxdWlyZTsKICAgICAgICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIChtb2QucmVxdWlyZSA9IGNvbnRleHQubWFrZVJlcXVpcmUobW9kLm1hcCkpOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAnZXhwb3J0cyc6IGZ1bmN0aW9uIChtb2QpIHsKICAgICAgICAgICAgICAgIG1vZC51c2luZ0V4cG9ydHMgPSB0cnVlOwogICAgICAgICAgICAgICAgaWYgKG1vZC5tYXAuaXNEZWZpbmUpIHsKICAgICAgICAgICAgICAgICAgICBpZiAobW9kLmV4cG9ydHMpIHsKICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIChkZWZpbmVkW21vZC5tYXAuaWRdID0gbW9kLmV4cG9ydHMpOwogICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiAobW9kLmV4cG9ydHMgPSBkZWZpbmVkW21vZC5tYXAuaWRdID0ge30pOwogICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgJ21vZHVsZSc6IGZ1bmN0aW9uIChtb2QpIHsKICAgICAgICAgICAgICAgIGlmIChtb2QubW9kdWxlKSB7CiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIG1vZC5tb2R1bGU7CiAgICAgICAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICAgICAgICAgIHJldHVybiAobW9kLm1vZHVsZSA9IHsKICAgICAgICAgICAgICAgICAgICAgICAgaWQ6IG1vZC5tYXAuaWQsCiAgICAgICAgICAgICAgICAgICAgICAgIHVyaTogbW9kLm1hcC51cmwsCiAgICAgICAgICAgICAgICAgICAgICAgIGNvbmZpZzogZnVuY3Rpb24gKCkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGdldE93bihjb25maWcuY29uZmlnLCBtb2QubWFwLmlkKSB8fCB7fTsKICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgZXhwb3J0czogbW9kLmV4cG9ydHMgfHwgKG1vZC5leHBvcnRzID0ge30pCiAgICAgICAgICAgICAgICAgICAgfSk7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICB9OwoKICAgICAgICBmdW5jdGlvbiBjbGVhblJlZ2lzdHJ5KGlkKSB7CiAgICAgICAgICAgIC8vQ2xlYW4gdXAgbWFjaGluZXJ5IHVzZWQgZm9yIHdhaXRpbmcgbW9kdWxlcy4KICAgICAgICAgICAgZGVsZXRlIHJlZ2lzdHJ5W2lkXTsKICAgICAgICAgICAgZGVsZXRlIGVuYWJsZWRSZWdpc3RyeVtpZF07CiAgICAgICAgfQoKICAgICAgICBmdW5jdGlvbiBicmVha0N5Y2xlKG1vZCwgdHJhY2VkLCBwcm9jZXNzZWQpIHsKICAgICAgICAgICAgdmFyIGlkID0gbW9kLm1hcC5pZDsKCiAgICAgICAgICAgIGlmIChtb2QuZXJyb3IpIHsKICAgICAgICAgICAgICAgIG1vZC5lbWl0KCdlcnJvcicsIG1vZC5lcnJvcik7CiAgICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgICAgICB0cmFjZWRbaWRdID0gdHJ1ZTsKICAgICAgICAgICAgICAgIGVhY2gobW9kLmRlcE1hcHMsIGZ1bmN0aW9uIChkZXBNYXAsIGkpIHsKICAgICAgICAgICAgICAgICAgICB2YXIgZGVwSWQgPSBkZXBNYXAuaWQsCiAgICAgICAgICAgICAgICAgICAgICAgIGRlcCA9IGdldE93bihyZWdpc3RyeSwgZGVwSWQpOwoKICAgICAgICAgICAgICAgICAgICAvL09ubHkgZm9yY2UgdGhpbmdzIHRoYXQgaGF2ZSBub3QgY29tcGxldGVkCiAgICAgICAgICAgICAgICAgICAgLy9iZWluZyBkZWZpbmVkLCBzbyBzdGlsbCBpbiB0aGUgcmVnaXN0cnksCiAgICAgICAgICAgICAgICAgICAgLy9hbmQgb25seSBpZiBpdCBoYXMgbm90IGJlZW4gbWF0Y2hlZCB1cAogICAgICAgICAgICAgICAgICAgIC8vaW4gdGhlIG1vZHVsZSBhbHJlYWR5LgogICAgICAgICAgICAgICAgICAgIGlmIChkZXAgJiYgIW1vZC5kZXBNYXRjaGVkW2ldICYmICFwcm9jZXNzZWRbZGVwSWRdKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChnZXRPd24odHJhY2VkLCBkZXBJZCkpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1vZC5kZWZpbmVEZXAoaSwgZGVmaW5lZFtkZXBJZF0pOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9kLmNoZWNrKCk7IC8vcGFzcyBmYWxzZT8KICAgICAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrQ3ljbGUoZGVwLCB0cmFjZWQsIHByb2Nlc3NlZCk7CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICB9KTsKICAgICAgICAgICAgICAgIHByb2Nlc3NlZFtpZF0gPSB0cnVlOwogICAgICAgICAgICB9CiAgICAgICAgfQoKICAgICAgICBmdW5jdGlvbiBjaGVja0xvYWRlZCgpIHsKICAgICAgICAgICAgdmFyIGVyciwgdXNpbmdQYXRoRmFsbGJhY2ssCiAgICAgICAgICAgICAgICB3YWl0SW50ZXJ2YWwgPSBjb25maWcud2FpdFNlY29uZHMgKiAxMDAwLAogICAgICAgICAgICAgICAgLy9JdCBpcyBwb3NzaWJsZSB0byBkaXNhYmxlIHRoZSB3YWl0IGludGVydmFsIGJ5IHVzaW5nIHdhaXRTZWNvbmRzIG9mIDAuCiAgICAgICAgICAgICAgICBleHBpcmVkID0gd2FpdEludGVydmFsICYmIChjb250ZXh0LnN0YXJ0VGltZSArIHdhaXRJbnRlcnZhbCkgPCBuZXcgRGF0ZSgpLmdldFRpbWUoKSwKICAgICAgICAgICAgICAgIG5vTG9hZHMgPSBbXSwKICAgICAgICAgICAgICAgIHJlcUNhbGxzID0gW10sCiAgICAgICAgICAgICAgICBzdGlsbExvYWRpbmcgPSBmYWxzZSwKICAgICAgICAgICAgICAgIG5lZWRDeWNsZUNoZWNrID0gdHJ1ZTsKCiAgICAgICAgICAgIC8vRG8gbm90IGJvdGhlciBpZiB0aGlzIGNhbGwgd2FzIGEgcmVzdWx0IG9mIGEgY3ljbGUgYnJlYWsuCiAgICAgICAgICAgIGlmIChpbkNoZWNrTG9hZGVkKSB7CiAgICAgICAgICAgICAgICByZXR1cm47CiAgICAgICAgICAgIH0KCiAgICAgICAgICAgIGluQ2hlY2tMb2FkZWQgPSB0cnVlOwoKICAgICAgICAgICAgLy9GaWd1cmUgb3V0IHRoZSBzdGF0ZSBvZiBhbGwgdGhlIG1vZHVsZXMuCiAgICAgICAgICAgIGVhY2hQcm9wKGVuYWJsZWRSZWdpc3RyeSwgZnVuY3Rpb24gKG1vZCkgewogICAgICAgICAgICAgICAgdmFyIG1hcCA9IG1vZC5tYXAsCiAgICAgICAgICAgICAgICAgICAgbW9kSWQgPSBtYXAuaWQ7CgogICAgICAgICAgICAgICAgLy9Ta2lwIHRoaW5ncyB0aGF0IGFyZSBub3QgZW5hYmxlZCBvciBpbiBlcnJvciBzdGF0ZS4KICAgICAgICAgICAgICAgIGlmICghbW9kLmVuYWJsZWQpIHsKICAgICAgICAgICAgICAgICAgICByZXR1cm47CiAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgaWYgKCFtYXAuaXNEZWZpbmUpIHsKICAgICAgICAgICAgICAgICAgICByZXFDYWxscy5wdXNoKG1vZCk7CiAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgaWYgKCFtb2QuZXJyb3IpIHsKICAgICAgICAgICAgICAgICAgICAvL0lmIHRoZSBtb2R1bGUgc2hvdWxkIGJlIGV4ZWN1dGVkLCBhbmQgaXQgaGFzIG5vdAogICAgICAgICAgICAgICAgICAgIC8vYmVlbiBpbml0ZWQgYW5kIHRpbWUgaXMgdXAsIHJlbWVtYmVyIGl0LgogICAgICAgICAgICAgICAgICAgIGlmICghbW9kLmluaXRlZCAmJiBleHBpcmVkKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChoYXNQYXRoRmFsbGJhY2sobW9kSWQpKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB1c2luZ1BhdGhGYWxsYmFjayA9IHRydWU7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdGlsbExvYWRpbmcgPSB0cnVlOwogICAgICAgICAgICAgICAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgbm9Mb2Fkcy5wdXNoKG1vZElkKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlbW92ZVNjcmlwdChtb2RJZCk7CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKCFtb2QuaW5pdGVkICYmIG1vZC5mZXRjaGVkICYmIG1hcC5pc0RlZmluZSkgewogICAgICAgICAgICAgICAgICAgICAgICBzdGlsbExvYWRpbmcgPSB0cnVlOwogICAgICAgICAgICAgICAgICAgICAgICBpZiAoIW1hcC5wcmVmaXgpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vTm8gcmVhc29uIHRvIGtlZXAgbG9va2luZyBmb3IgdW5maW5pc2hlZAogICAgICAgICAgICAgICAgICAgICAgICAgICAgLy9sb2FkaW5nLiBJZiB0aGUgb25seSBzdGlsbExvYWRpbmcgaXMgYQogICAgICAgICAgICAgICAgICAgICAgICAgICAgLy9wbHVnaW4gcmVzb3VyY2UgdGhvdWdoLCBrZWVwIGdvaW5nLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgLy9iZWNhdXNlIGl0IG1heSBiZSB0aGF0IGEgcGx1Z2luIHJlc291cmNlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvL2lzIHdhaXRpbmcgb24gYSBub24tcGx1Z2luIGN5Y2xlLgogICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIChuZWVkQ3ljbGVDaGVjayA9IGZhbHNlKTsKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSk7CgogICAgICAgICAgICBpZiAoZXhwaXJlZCAmJiBub0xvYWRzLmxlbmd0aCkgewogICAgICAgICAgICAgICAgLy9JZiB3YWl0IHRpbWUgZXhwaXJlZCwgdGhyb3cgZXJyb3Igb2YgdW5sb2FkZWQgbW9kdWxlcy4KICAgICAgICAgICAgICAgIGVyciA9IG1ha2VFcnJvcigndGltZW91dCcsICdMb2FkIHRpbWVvdXQgZm9yIG1vZHVsZXM6ICcgKyBub0xvYWRzLCBudWxsLCBub0xvYWRzKTsKICAgICAgICAgICAgICAgIGVyci5jb250ZXh0TmFtZSA9IGNvbnRleHQuY29udGV4dE5hbWU7CiAgICAgICAgICAgICAgICByZXR1cm4gb25FcnJvcihlcnIpOwogICAgICAgICAgICB9CgogICAgICAgICAgICAvL05vdCBleHBpcmVkLCBjaGVjayBmb3IgYSBjeWNsZS4KICAgICAgICAgICAgaWYgKG5lZWRDeWNsZUNoZWNrKSB7CiAgICAgICAgICAgICAgICBlYWNoKHJlcUNhbGxzLCBmdW5jdGlvbiAobW9kKSB7CiAgICAgICAgICAgICAgICAgICAgYnJlYWtDeWNsZShtb2QsIHt9LCB7fSk7CiAgICAgICAgICAgICAgICB9KTsKICAgICAgICAgICAgfQoKICAgICAgICAgICAgLy9JZiBzdGlsbCB3YWl0aW5nIG9uIGxvYWRzLCBhbmQgdGhlIHdhaXRpbmcgbG9hZCBpcyBzb21ldGhpbmcKICAgICAgICAgICAgLy9vdGhlciB0aGFuIGEgcGx1Z2luIHJlc291cmNlLCBvciB0aGVyZSBhcmUgc3RpbGwgb3V0c3RhbmRpbmcKICAgICAgICAgICAgLy9zY3JpcHRzLCB0aGVuIGp1c3QgdHJ5IGJhY2sgbGF0ZXIuCiAgICAgICAgICAgIGlmICgoIWV4cGlyZWQgfHwgdXNpbmdQYXRoRmFsbGJhY2spICYmIHN0aWxsTG9hZGluZykgewogICAgICAgICAgICAgICAgLy9Tb21ldGhpbmcgaXMgc3RpbGwgd2FpdGluZyB0byBsb2FkLiBXYWl0IGZvciBpdCwgYnV0IG9ubHkKICAgICAgICAgICAgICAgIC8vaWYgYSB0aW1lb3V0IGlzIG5vdCBhbHJlYWR5IGluIGVmZmVjdC4KICAgICAgICAgICAgICAgIGlmICgoaXNCcm93c2VyIHx8IGlzV2ViV29ya2VyKSAmJiAhY2hlY2tMb2FkZWRUaW1lb3V0SWQpIHsKICAgICAgICAgICAgICAgICAgICBjaGVja0xvYWRlZFRpbWVvdXRJZCA9IHNldFRpbWVvdXQoZnVuY3Rpb24gKCkgewogICAgICAgICAgICAgICAgICAgICAgICBjaGVja0xvYWRlZFRpbWVvdXRJZCA9IDA7CiAgICAgICAgICAgICAgICAgICAgICAgIGNoZWNrTG9hZGVkKCk7CiAgICAgICAgICAgICAgICAgICAgfSwgNTApOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CgogICAgICAgICAgICBpbkNoZWNrTG9hZGVkID0gZmFsc2U7CiAgICAgICAgfQoKICAgICAgICBNb2R1bGUgPSBmdW5jdGlvbiAobWFwKSB7CiAgICAgICAgICAgIHRoaXMuZXZlbnRzID0gZ2V0T3duKHVuZGVmRXZlbnRzLCBtYXAuaWQpIHx8IHt9OwogICAgICAgICAgICB0aGlzLm1hcCA9IG1hcDsKICAgICAgICAgICAgdGhpcy5zaGltID0gZ2V0T3duKGNvbmZpZy5zaGltLCBtYXAuaWQpOwogICAgICAgICAgICB0aGlzLmRlcEV4cG9ydHMgPSBbXTsKICAgICAgICAgICAgdGhpcy5kZXBNYXBzID0gW107CiAgICAgICAgICAgIHRoaXMuZGVwTWF0Y2hlZCA9IFtdOwogICAgICAgICAgICB0aGlzLnBsdWdpbk1hcHMgPSB7fTsKICAgICAgICAgICAgdGhpcy5kZXBDb3VudCA9IDA7CgogICAgICAgICAgICAvKiB0aGlzLmV4cG9ydHMgdGhpcy5mYWN0b3J5CiAgICAgICAgICAgICAgIHRoaXMuZGVwTWFwcyA9IFtdLAogICAgICAgICAgICAgICB0aGlzLmVuYWJsZWQsIHRoaXMuZmV0Y2hlZAogICAgICAgICAgICAqLwogICAgICAgIH07CgogICAgICAgIE1vZHVsZS5wcm90b3R5cGUgPSB7CiAgICAgICAgICAgIGluaXQ6IGZ1bmN0aW9uIChkZXBNYXBzLCBmYWN0b3J5LCBlcnJiYWNrLCBvcHRpb25zKSB7CiAgICAgICAgICAgICAgICBvcHRpb25zID0gb3B0aW9ucyB8fCB7fTsKCiAgICAgICAgICAgICAgICAvL0RvIG5vdCBkbyBtb3JlIGluaXRzIGlmIGFscmVhZHkgZG9uZS4gQ2FuIGhhcHBlbiBpZiB0aGVyZQogICAgICAgICAgICAgICAgLy9hcmUgbXVsdGlwbGUgZGVmaW5lIGNhbGxzIGZvciB0aGUgc2FtZSBtb2R1bGUuIFRoYXQgaXMgbm90CiAgICAgICAgICAgICAgICAvL2Egbm9ybWFsLCBjb21tb24gY2FzZSwgYnV0IGl0IGlzIGFsc28gbm90IHVuZXhwZWN0ZWQuCiAgICAgICAgICAgICAgICBpZiAodGhpcy5pbml0ZWQpIHsKICAgICAgICAgICAgICAgICAgICByZXR1cm47CiAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgdGhpcy5mYWN0b3J5ID0gZmFjdG9yeTsKCiAgICAgICAgICAgICAgICBpZiAoZXJyYmFjaykgewogICAgICAgICAgICAgICAgICAgIC8vUmVnaXN0ZXIgZm9yIGVycm9ycyBvbiB0aGlzIG1vZHVsZS4KICAgICAgICAgICAgICAgICAgICB0aGlzLm9uKCdlcnJvcicsIGVycmJhY2spOwogICAgICAgICAgICAgICAgfSBlbHNlIGlmICh0aGlzLmV2ZW50cy5lcnJvcikgewogICAgICAgICAgICAgICAgICAgIC8vSWYgbm8gZXJyYmFjayBhbHJlYWR5LCBidXQgdGhlcmUgYXJlIGVycm9yIGxpc3RlbmVycwogICAgICAgICAgICAgICAgICAgIC8vb24gdGhpcyBtb2R1bGUsIHNldCB1cCBhbiBlcnJiYWNrIHRvIHBhc3MgdG8gdGhlIGRlcHMuCiAgICAgICAgICAgICAgICAgICAgZXJyYmFjayA9IGJpbmQodGhpcywgZnVuY3Rpb24gKGVycikgewogICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmVtaXQoJ2Vycm9yJywgZXJyKTsKICAgICAgICAgICAgICAgICAgICB9KTsKICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICAvL0RvIGEgY29weSBvZiB0aGUgZGVwZW5kZW5jeSBhcnJheSwgc28gdGhhdAogICAgICAgICAgICAgICAgLy9zb3VyY2UgaW5wdXRzIGFyZSBub3QgbW9kaWZpZWQuIEZvciBleGFtcGxlCiAgICAgICAgICAgICAgICAvLyJzaGltIiBkZXBzIGFyZSBwYXNzZWQgaW4gaGVyZSBkaXJlY3RseSwgYW5kCiAgICAgICAgICAgICAgICAvL2RvaW5nIGEgZGlyZWN0IG1vZGlmaWNhdGlvbiBvZiB0aGUgZGVwTWFwcyBhcnJheQogICAgICAgICAgICAgICAgLy93b3VsZCBhZmZlY3QgdGhhdCBjb25maWcuCiAgICAgICAgICAgICAgICB0aGlzLmRlcE1hcHMgPSBkZXBNYXBzICYmIGRlcE1hcHMuc2xpY2UoMCk7CgogICAgICAgICAgICAgICAgdGhpcy5lcnJiYWNrID0gZXJyYmFjazsKCiAgICAgICAgICAgICAgICAvL0luZGljYXRlIHRoaXMgbW9kdWxlIGhhcyBiZSBpbml0aWFsaXplZAogICAgICAgICAgICAgICAgdGhpcy5pbml0ZWQgPSB0cnVlOwoKICAgICAgICAgICAgICAgIHRoaXMuaWdub3JlID0gb3B0aW9ucy5pZ25vcmU7CgogICAgICAgICAgICAgICAgLy9Db3VsZCBoYXZlIG9wdGlvbiB0byBpbml0IHRoaXMgbW9kdWxlIGluIGVuYWJsZWQgbW9kZSwKICAgICAgICAgICAgICAgIC8vb3IgY291bGQgaGF2ZSBiZWVuIHByZXZpb3VzbHkgbWFya2VkIGFzIGVuYWJsZWQuIEhvd2V2ZXIsCiAgICAgICAgICAgICAgICAvL3RoZSBkZXBlbmRlbmNpZXMgYXJlIG5vdCBrbm93biB1bnRpbCBpbml0IGlzIGNhbGxlZC4gU28KICAgICAgICAgICAgICAgIC8vaWYgZW5hYmxlZCBwcmV2aW91c2x5LCBub3cgdHJpZ2dlciBkZXBlbmRlbmNpZXMgYXMgZW5hYmxlZC4KICAgICAgICAgICAgICAgIGlmIChvcHRpb25zLmVuYWJsZWQgfHwgdGhpcy5lbmFibGVkKSB7CiAgICAgICAgICAgICAgICAgICAgLy9FbmFibGUgdGhpcyBtb2R1bGUgYW5kIGRlcGVuZGVuY2llcy4KICAgICAgICAgICAgICAgICAgICAvL1dpbGwgY2FsbCB0aGlzLmNoZWNrKCkKICAgICAgICAgICAgICAgICAgICB0aGlzLmVuYWJsZSgpOwogICAgICAgICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgICAgICAgICB0aGlzLmNoZWNrKCk7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCgogICAgICAgICAgICBkZWZpbmVEZXA6IGZ1bmN0aW9uIChpLCBkZXBFeHBvcnRzKSB7CiAgICAgICAgICAgICAgICAvL0JlY2F1c2Ugb2YgY3ljbGVzLCBkZWZpbmVkIGNhbGxiYWNrIGZvciBhIGdpdmVuCiAgICAgICAgICAgICAgICAvL2V4cG9ydCBjYW4gYmUgY2FsbGVkIG1vcmUgdGhhbiBvbmNlLgogICAgICAgICAgICAgICAgaWYgKCF0aGlzLmRlcE1hdGNoZWRbaV0pIHsKICAgICAgICAgICAgICAgICAgICB0aGlzLmRlcE1hdGNoZWRbaV0gPSB0cnVlOwogICAgICAgICAgICAgICAgICAgIHRoaXMuZGVwQ291bnQgLT0gMTsKICAgICAgICAgICAgICAgICAgICB0aGlzLmRlcEV4cG9ydHNbaV0gPSBkZXBFeHBvcnRzOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAoKICAgICAgICAgICAgZmV0Y2g6IGZ1bmN0aW9uICgpIHsKICAgICAgICAgICAgICAgIGlmICh0aGlzLmZldGNoZWQpIHsKICAgICAgICAgICAgICAgICAgICByZXR1cm47CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICB0aGlzLmZldGNoZWQgPSB0cnVlOwoKICAgICAgICAgICAgICAgIGNvbnRleHQuc3RhcnRUaW1lID0gKG5ldyBEYXRlKCkpLmdldFRpbWUoKTsKCiAgICAgICAgICAgICAgICB2YXIgbWFwID0gdGhpcy5tYXA7CgogICAgICAgICAgICAgICAgLy9JZiB0aGUgbWFuYWdlciBpcyBmb3IgYSBwbHVnaW4gbWFuYWdlZCByZXNvdXJjZSwKICAgICAgICAgICAgICAgIC8vYXNrIHRoZSBwbHVnaW4gdG8gbG9hZCBpdCBub3cuCiAgICAgICAgICAgICAgICBpZiAodGhpcy5zaGltKSB7CiAgICAgICAgICAgICAgICAgICAgY29udGV4dC5tYWtlUmVxdWlyZSh0aGlzLm1hcCwgewogICAgICAgICAgICAgICAgICAgICAgICBlbmFibGVCdWlsZENhbGxiYWNrOiB0cnVlCiAgICAgICAgICAgICAgICAgICAgfSkodGhpcy5zaGltLmRlcHMgfHwgW10sIGJpbmQodGhpcywgZnVuY3Rpb24gKCkgewogICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gbWFwLnByZWZpeCA/IHRoaXMuY2FsbFBsdWdpbigpIDogdGhpcy5sb2FkKCk7CiAgICAgICAgICAgICAgICAgICAgfSkpOwogICAgICAgICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgICAgICAgICAvL1JlZ3VsYXIgZGVwZW5kZW5jeS4KICAgICAgICAgICAgICAgICAgICByZXR1cm4gbWFwLnByZWZpeCA/IHRoaXMuY2FsbFBsdWdpbigpIDogdGhpcy5sb2FkKCk7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCgogICAgICAgICAgICBsb2FkOiBmdW5jdGlvbiAoKSB7CiAgICAgICAgICAgICAgICB2YXIgdXJsID0gdGhpcy5tYXAudXJsOwoKICAgICAgICAgICAgICAgIC8vUmVndWxhciBkZXBlbmRlbmN5LgogICAgICAgICAgICAgICAgaWYgKCF1cmxGZXRjaGVkW3VybF0pIHsKICAgICAgICAgICAgICAgICAgICB1cmxGZXRjaGVkW3VybF0gPSB0cnVlOwogICAgICAgICAgICAgICAgICAgIGNvbnRleHQubG9hZCh0aGlzLm1hcC5pZCwgdXJsKTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKCiAgICAgICAgICAgIC8qKgogICAgICAgICAgICAgKiBDaGVja3MgaWYgdGhlIG1vZHVsZSBpcyByZWFkeSB0byBkZWZpbmUgaXRzZWxmLCBhbmQgaWYgc28sCiAgICAgICAgICAgICAqIGRlZmluZSBpdC4KICAgICAgICAgICAgICovCiAgICAgICAgICAgIGNoZWNrOiBmdW5jdGlvbiAoKSB7CiAgICAgICAgICAgICAgICBpZiAoIXRoaXMuZW5hYmxlZCB8fCB0aGlzLmVuYWJsaW5nKSB7CiAgICAgICAgICAgICAgICAgICAgcmV0dXJuOwogICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgIHZhciBlcnIsIGNqc01vZHVsZSwKICAgICAgICAgICAgICAgICAgICBpZCA9IHRoaXMubWFwLmlkLAogICAgICAgICAgICAgICAgICAgIGRlcEV4cG9ydHMgPSB0aGlzLmRlcEV4cG9ydHMsCiAgICAgICAgICAgICAgICAgICAgZXhwb3J0cyA9IHRoaXMuZXhwb3J0cywKICAgICAgICAgICAgICAgICAgICBmYWN0b3J5ID0gdGhpcy5mYWN0b3J5OwoKICAgICAgICAgICAgICAgIGlmICghdGhpcy5pbml0ZWQpIHsKICAgICAgICAgICAgICAgICAgICAvLyBPbmx5IGZldGNoIGlmIG5vdCBhbHJlYWR5IGluIHRoZSBkZWZRdWV1ZS4KICAgICAgICAgICAgICAgICAgICBpZiAoIWhhc1Byb3AoY29udGV4dC5kZWZRdWV1ZU1hcCwgaWQpKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuZmV0Y2goKTsKICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKHRoaXMuZXJyb3IpIHsKICAgICAgICAgICAgICAgICAgICB0aGlzLmVtaXQoJ2Vycm9yJywgdGhpcy5lcnJvcik7CiAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKCF0aGlzLmRlZmluaW5nKSB7CiAgICAgICAgICAgICAgICAgICAgLy9UaGUgZmFjdG9yeSBjb3VsZCB0cmlnZ2VyIGFub3RoZXIgcmVxdWlyZSBjYWxsCiAgICAgICAgICAgICAgICAgICAgLy90aGF0IHdvdWxkIHJlc3VsdCBpbiBjaGVja2luZyB0aGlzIG1vZHVsZSB0bwogICAgICAgICAgICAgICAgICAgIC8vZGVmaW5lIGl0c2VsZiBhZ2Fpbi4gSWYgYWxyZWFkeSBpbiB0aGUgcHJvY2VzcwogICAgICAgICAgICAgICAgICAgIC8vb2YgZG9pbmcgdGhhdCwgc2tpcCB0aGlzIHdvcmsuCiAgICAgICAgICAgICAgICAgICAgdGhpcy5kZWZpbmluZyA9IHRydWU7CgogICAgICAgICAgICAgICAgICAgIGlmICh0aGlzLmRlcENvdW50IDwgMSAmJiAhdGhpcy5kZWZpbmVkKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChpc0Z1bmN0aW9uKGZhY3RvcnkpKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0cnkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4cG9ydHMgPSBjb250ZXh0LmV4ZWNDYihpZCwgZmFjdG9yeSwgZGVwRXhwb3J0cywgZXhwb3J0cyk7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IGNhdGNoIChlKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZXJyID0gZTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBGYXZvciByZXR1cm4gdmFsdWUgb3ZlciBleHBvcnRzLiBJZiBub2RlL2NqcyBpbiBwbGF5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gdGhlbiB3aWxsIG5vdCBoYXZlIGEgcmV0dXJuIHZhbHVlIGFueXdheS4gRmF2b3IKICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIG1vZHVsZS5leHBvcnRzIGFzc2lnbm1lbnQgb3ZlciBleHBvcnRzIG9iamVjdC4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmICh0aGlzLm1hcC5pc0RlZmluZSAmJiBleHBvcnRzID09PSB1bmRlZmluZWQpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjanNNb2R1bGUgPSB0aGlzLm1vZHVsZTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAoY2pzTW9kdWxlKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4cG9ydHMgPSBjanNNb2R1bGUuZXhwb3J0czsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKHRoaXMudXNpbmdFeHBvcnRzKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vZXhwb3J0cyBhbHJlYWR5IHNldCB0aGUgZGVmaW5lZCB2YWx1ZS4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZXhwb3J0cyA9IHRoaXMuZXhwb3J0czsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGVycikgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIElmIHRoZXJlIGlzIGFuIGVycm9yIGxpc3RlbmVyLCBmYXZvciBwYXNzaW5nCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gdG8gdGhhdCBpbnN0ZWFkIG9mIHRocm93aW5nIGFuIGVycm9yLiBIb3dldmVyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIG9ubHkgZG8gaXQgZm9yIGRlZmluZSgpJ2QgIG1vZHVsZXMuIHJlcXVpcmUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBlcnJiYWNrcyBzaG91bGQgbm90IGJlIGNhbGxlZCBmb3IgZmFpbHVyZXMgaW4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyB0aGVpciBjYWxsYmFja3MgKCM2OTkpLiBIb3dldmVyIGlmIGEgZ2xvYmFsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gb25FcnJvciBpcyBzZXQsIHVzZSB0aGF0LgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmICgodGhpcy5ldmVudHMuZXJyb3IgJiYgdGhpcy5tYXAuaXNEZWZpbmUpIHx8CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcS5vbkVycm9yICE9PSBkZWZhdWx0T25FcnJvcikgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlcnIucmVxdWlyZU1hcCA9IHRoaXMubWFwOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlcnIucmVxdWlyZU1vZHVsZXMgPSB0aGlzLm1hcC5pc0RlZmluZSA/IFt0aGlzLm1hcC5pZF0gOiBudWxsOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlcnIucmVxdWlyZVR5cGUgPSB0aGlzLm1hcC5pc0RlZmluZSA/ICdkZWZpbmUnIDogJ3JlcXVpcmUnOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gb25FcnJvcigodGhpcy5lcnJvciA9IGVycikpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAodHlwZW9mIGNvbnNvbGUgIT09ICd1bmRlZmluZWQnICYmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIExvZyB0aGUgZXJyb3IgZm9yIGRlYnVnZ2luZy4gSWYgcHJvbWlzZXMgY291bGQgYmUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gdXNlZCwgdGhpcyB3b3VsZCBiZSBkaWZmZXJlbnQsIGJ1dCBtYWtpbmcgZG8uCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoZXJyKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBEbyBub3Qgd2FudCB0byBjb21wbGV0ZWx5IGxvc2UgdGhlIGVycm9yLiBXaGlsZSB0aGlzCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIHdpbGwgbWVzcyB1cCBwcm9jZXNzaW5nIGFuZCBsZWFkIHRvIHNpbWlsYXIgcmVzdWx0cwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBhcyBidWcgMTQ0MCwgaXQgYXQgbGVhc3Qgc3VyZmFjZXMgdGhlIGVycm9yLgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXEub25FcnJvcihlcnIpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vSnVzdCBhIGxpdGVyYWwgdmFsdWUKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4cG9ydHMgPSBmYWN0b3J5OwogICAgICAgICAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmV4cG9ydHMgPSBleHBvcnRzOwoKICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHRoaXMubWFwLmlzRGVmaW5lICYmICF0aGlzLmlnbm9yZSkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVmaW5lZFtpZF0gPSBleHBvcnRzOwoKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIChyZXEub25SZXNvdXJjZUxvYWQpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXIgcmVzTG9hZE1hcHMgPSBbXTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlYWNoKHRoaXMuZGVwTWFwcywgZnVuY3Rpb24gKGRlcE1hcCkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXNMb2FkTWFwcy5wdXNoKGRlcE1hcC5ub3JtYWxpemVkTWFwIHx8IGRlcE1hcCk7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSk7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVxLm9uUmVzb3VyY2VMb2FkKGNvbnRleHQsIHRoaXMubWFwLCByZXNMb2FkTWFwcyk7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICAgICAgICAgIC8vQ2xlYW4gdXAKICAgICAgICAgICAgICAgICAgICAgICAgY2xlYW5SZWdpc3RyeShpZCk7CgogICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmRlZmluZWQgPSB0cnVlOwogICAgICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICAgICAgLy9GaW5pc2hlZCB0aGUgZGVmaW5lIHN0YWdlLiBBbGxvdyBjYWxsaW5nIGNoZWNrIGFnYWluCiAgICAgICAgICAgICAgICAgICAgLy90byBhbGxvdyBkZWZpbmUgbm90aWZpY2F0aW9ucyBiZWxvdyBpbiB0aGUgY2FzZSBvZiBhCiAgICAgICAgICAgICAgICAgICAgLy9jeWNsZS4KICAgICAgICAgICAgICAgICAgICB0aGlzLmRlZmluaW5nID0gZmFsc2U7CgogICAgICAgICAgICAgICAgICAgIGlmICh0aGlzLmRlZmluZWQgJiYgIXRoaXMuZGVmaW5lRW1pdHRlZCkgewogICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmRlZmluZUVtaXR0ZWQgPSB0cnVlOwogICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmVtaXQoJ2RlZmluZWQnLCB0aGlzLmV4cG9ydHMpOwogICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmRlZmluZUVtaXRDb21wbGV0ZSA9IHRydWU7CiAgICAgICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKCiAgICAgICAgICAgIGNhbGxQbHVnaW46IGZ1bmN0aW9uICgpIHsKICAgICAgICAgICAgICAgIHZhciBtYXAgPSB0aGlzLm1hcCwKICAgICAgICAgICAgICAgICAgICBpZCA9IG1hcC5pZCwKICAgICAgICAgICAgICAgICAgICAvL01hcCBhbHJlYWR5IG5vcm1hbGl6ZWQgdGhlIHByZWZpeC4KICAgICAgICAgICAgICAgICAgICBwbHVnaW5NYXAgPSBtYWtlTW9kdWxlTWFwKG1hcC5wcmVmaXgpOwoKICAgICAgICAgICAgICAgIC8vTWFyayB0aGlzIGFzIGEgZGVwZW5kZW5jeSBmb3IgdGhpcyBwbHVnaW4sIHNvIGl0CiAgICAgICAgICAgICAgICAvL2NhbiBiZSB0cmFjZWQgZm9yIGN5Y2xlcy4KICAgICAgICAgICAgICAgIHRoaXMuZGVwTWFwcy5wdXNoKHBsdWdpbk1hcCk7CgogICAgICAgICAgICAgICAgb24ocGx1Z2luTWFwLCAnZGVmaW5lZCcsIGJpbmQodGhpcywgZnVuY3Rpb24gKHBsdWdpbikgewogICAgICAgICAgICAgICAgICAgIHZhciBsb2FkLCBub3JtYWxpemVkTWFwLCBub3JtYWxpemVkTW9kLAogICAgICAgICAgICAgICAgICAgICAgICBidW5kbGVJZCA9IGdldE93bihidW5kbGVzTWFwLCB0aGlzLm1hcC5pZCksCiAgICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSB0aGlzLm1hcC5uYW1lLAogICAgICAgICAgICAgICAgICAgICAgICBwYXJlbnROYW1lID0gdGhpcy5tYXAucGFyZW50TWFwID8gdGhpcy5tYXAucGFyZW50TWFwLm5hbWUgOiBudWxsLAogICAgICAgICAgICAgICAgICAgICAgICBsb2NhbFJlcXVpcmUgPSBjb250ZXh0Lm1ha2VSZXF1aXJlKG1hcC5wYXJlbnRNYXAsIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVuYWJsZUJ1aWxkQ2FsbGJhY2s6IHRydWUKICAgICAgICAgICAgICAgICAgICAgICAgfSk7CgogICAgICAgICAgICAgICAgICAgIC8vSWYgY3VycmVudCBtYXAgaXMgbm90IG5vcm1hbGl6ZWQsIHdhaXQgZm9yIHRoYXQKICAgICAgICAgICAgICAgICAgICAvL25vcm1hbGl6ZWQgbmFtZSB0byBsb2FkIGluc3RlYWQgb2YgY29udGludWluZy4KICAgICAgICAgICAgICAgICAgICBpZiAodGhpcy5tYXAudW5ub3JtYWxpemVkKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIC8vTm9ybWFsaXplIHRoZSBJRCBpZiB0aGUgcGx1Z2luIGFsbG93cyBpdC4KICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHBsdWdpbi5ub3JtYWxpemUpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSBwbHVnaW4ubm9ybWFsaXplKG5hbWUsIGZ1bmN0aW9uIChuYW1lKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIG5vcm1hbGl6ZShuYW1lLCBwYXJlbnROYW1lLCB0cnVlKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0pIHx8ICcnOwogICAgICAgICAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgICAgICAgICAvL3ByZWZpeCBhbmQgbmFtZSBzaG91bGQgYWxyZWFkeSBiZSBub3JtYWxpemVkLCBubyBuZWVkCiAgICAgICAgICAgICAgICAgICAgICAgIC8vZm9yIGFwcGx5aW5nIG1hcCBjb25maWcgYWdhaW4gZWl0aGVyLgogICAgICAgICAgICAgICAgICAgICAgICBub3JtYWxpemVkTWFwID0gbWFrZU1vZHVsZU1hcChtYXAucHJlZml4ICsgJyEnICsgbmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5tYXAucGFyZW50TWFwKTsKICAgICAgICAgICAgICAgICAgICAgICAgb24obm9ybWFsaXplZE1hcCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICdkZWZpbmVkJywgYmluZCh0aGlzLCBmdW5jdGlvbiAodmFsdWUpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGlzLm1hcC5ub3JtYWxpemVkTWFwID0gbm9ybWFsaXplZE1hcDsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmluaXQoW10sIGZ1bmN0aW9uICgpIHsgcmV0dXJuIHZhbHVlOyB9LCBudWxsLCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVuYWJsZWQ6IHRydWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlnbm9yZTogdHJ1ZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0pOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgfSkpOwoKICAgICAgICAgICAgICAgICAgICAgICAgbm9ybWFsaXplZE1vZCA9IGdldE93bihyZWdpc3RyeSwgbm9ybWFsaXplZE1hcC5pZCk7CiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChub3JtYWxpemVkTW9kKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvL01hcmsgdGhpcyBhcyBhIGRlcGVuZGVuY3kgZm9yIHRoaXMgcGx1Z2luLCBzbyBpdAogICAgICAgICAgICAgICAgICAgICAgICAgICAgLy9jYW4gYmUgdHJhY2VkIGZvciBjeWNsZXMuCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmRlcE1hcHMucHVzaChub3JtYWxpemVkTWFwKTsKCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAodGhpcy5ldmVudHMuZXJyb3IpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBub3JtYWxpemVkTW9kLm9uKCdlcnJvcicsIGJpbmQodGhpcywgZnVuY3Rpb24gKGVycikgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmVtaXQoJ2Vycm9yJywgZXJyKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9KSk7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBub3JtYWxpemVkTW9kLmVuYWJsZSgpOwogICAgICAgICAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgICAgICAgICByZXR1cm47CiAgICAgICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgICAgICAvL0lmIGEgcGF0aHMgY29uZmlnLCB0aGVuIGp1c3QgbG9hZCB0aGF0IGZpbGUgaW5zdGVhZCB0bwogICAgICAgICAgICAgICAgICAgIC8vcmVzb2x2ZSB0aGUgcGx1Z2luLCBhcyBpdCBpcyBidWlsdCBpbnRvIHRoYXQgcGF0aHMgbGF5ZXIuCiAgICAgICAgICAgICAgICAgICAgaWYgKGJ1bmRsZUlkKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMubWFwLnVybCA9IGNvbnRleHQubmFtZVRvVXJsKGJ1bmRsZUlkKTsKICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5sb2FkKCk7CiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybjsKICAgICAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgICAgIGxvYWQgPSBiaW5kKHRoaXMsIGZ1bmN0aW9uICh2YWx1ZSkgewogICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmluaXQoW10sIGZ1bmN0aW9uICgpIHsgcmV0dXJuIHZhbHVlOyB9LCBudWxsLCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbmFibGVkOiB0cnVlCiAgICAgICAgICAgICAgICAgICAgICAgIH0pOwogICAgICAgICAgICAgICAgICAgIH0pOwoKICAgICAgICAgICAgICAgICAgICBsb2FkLmVycm9yID0gYmluZCh0aGlzLCBmdW5jdGlvbiAoZXJyKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuaW5pdGVkID0gdHJ1ZTsKICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5lcnJvciA9IGVycjsKICAgICAgICAgICAgICAgICAgICAgICAgZXJyLnJlcXVpcmVNb2R1bGVzID0gW2lkXTsKCiAgICAgICAgICAgICAgICAgICAgICAgIC8vUmVtb3ZlIHRlbXAgdW5ub3JtYWxpemVkIG1vZHVsZXMgZm9yIHRoaXMgbW9kdWxlLAogICAgICAgICAgICAgICAgICAgICAgICAvL3NpbmNlIHRoZXkgd2lsbCBuZXZlciBiZSByZXNvbHZlZCBvdGhlcndpc2Ugbm93LgogICAgICAgICAgICAgICAgICAgICAgICBlYWNoUHJvcChyZWdpc3RyeSwgZnVuY3Rpb24gKG1vZCkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKG1vZC5tYXAuaWQuaW5kZXhPZihpZCArICdfdW5ub3JtYWxpemVkJykgPT09IDApIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbGVhblJlZ2lzdHJ5KG1vZC5tYXAuaWQpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9KTsKCiAgICAgICAgICAgICAgICAgICAgICAgIG9uRXJyb3IoZXJyKTsKICAgICAgICAgICAgICAgICAgICB9KTsKCiAgICAgICAgICAgICAgICAgICAgLy9BbGxvdyBwbHVnaW5zIHRvIGxvYWQgb3RoZXIgY29kZSB3aXRob3V0IGhhdmluZyB0byBrbm93IHRoZQogICAgICAgICAgICAgICAgICAgIC8vY29udGV4dCBvciBob3cgdG8gJ2NvbXBsZXRlJyB0aGUgbG9hZC4KICAgICAgICAgICAgICAgICAgICBsb2FkLmZyb21UZXh0ID0gYmluZCh0aGlzLCBmdW5jdGlvbiAodGV4dCwgdGV4dEFsdCkgewogICAgICAgICAgICAgICAgICAgICAgICAvKmpzbGludCBldmlsOiB0cnVlICovCiAgICAgICAgICAgICAgICAgICAgICAgIHZhciBtb2R1bGVOYW1lID0gbWFwLm5hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtb2R1bGVNYXAgPSBtYWtlTW9kdWxlTWFwKG1vZHVsZU5hbWUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgaGFzSW50ZXJhY3RpdmUgPSB1c2VJbnRlcmFjdGl2ZTsKCiAgICAgICAgICAgICAgICAgICAgICAgIC8vQXMgb2YgMi4xLjAsIHN1cHBvcnQganVzdCBwYXNzaW5nIHRoZSB0ZXh0LCB0byByZWluZm9yY2UKICAgICAgICAgICAgICAgICAgICAgICAgLy9mcm9tVGV4dCBvbmx5IGJlaW5nIGNhbGxlZCBvbmNlIHBlciByZXNvdXJjZS4gU3RpbGwKICAgICAgICAgICAgICAgICAgICAgICAgLy9zdXBwb3J0IG9sZCBzdHlsZSBvZiBwYXNzaW5nIG1vZHVsZU5hbWUgYnV0IGRpc2NhcmQKICAgICAgICAgICAgICAgICAgICAgICAgLy90aGF0IG1vZHVsZU5hbWUgaW4gZmF2b3Igb2YgdGhlIGludGVybmFsIHJlZi4KICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHRleHRBbHQpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRleHQgPSB0ZXh0QWx0OwogICAgICAgICAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgICAgICAgICAvL1R1cm4gb2ZmIGludGVyYWN0aXZlIHNjcmlwdCBtYXRjaGluZyBmb3IgSUUgZm9yIGFueSBkZWZpbmUKICAgICAgICAgICAgICAgICAgICAgICAgLy9jYWxscyBpbiB0aGUgdGV4dCwgdGhlbiB0dXJuIGl0IGJhY2sgb24gYXQgdGhlIGVuZC4KICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGhhc0ludGVyYWN0aXZlKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB1c2VJbnRlcmFjdGl2ZSA9IGZhbHNlOwogICAgICAgICAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgICAgICAgICAvL1ByaW1lIHRoZSBzeXN0ZW0gYnkgY3JlYXRpbmcgYSBtb2R1bGUgaW5zdGFuY2UgZm9yCiAgICAgICAgICAgICAgICAgICAgICAgIC8vaXQuCiAgICAgICAgICAgICAgICAgICAgICAgIGdldE1vZHVsZShtb2R1bGVNYXApOwoKICAgICAgICAgICAgICAgICAgICAgICAgLy9UcmFuc2ZlciBhbnkgY29uZmlnIHRvIHRoaXMgb3RoZXIgbW9kdWxlLgogICAgICAgICAgICAgICAgICAgICAgICBpZiAoaGFzUHJvcChjb25maWcuY29uZmlnLCBpZCkpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbmZpZy5jb25maWdbbW9kdWxlTmFtZV0gPSBjb25maWcuY29uZmlnW2lkXTsKICAgICAgICAgICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgICAgICAgICAgdHJ5IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcS5leGVjKHRleHQpOwogICAgICAgICAgICAgICAgICAgICAgICB9IGNhdGNoIChlKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gb25FcnJvcihtYWtlRXJyb3IoJ2Zyb210ZXh0ZXZhbCcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdmcm9tVGV4dCBldmFsIGZvciAnICsgaWQgKwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICcgZmFpbGVkOiAnICsgZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgW2lkXSkpOwogICAgICAgICAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgICAgICAgICBpZiAoaGFzSW50ZXJhY3RpdmUpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVzZUludGVyYWN0aXZlID0gdHJ1ZTsKICAgICAgICAgICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgICAgICAgICAgLy9NYXJrIHRoaXMgYXMgYSBkZXBlbmRlbmN5IGZvciB0aGUgcGx1Z2luCiAgICAgICAgICAgICAgICAgICAgICAgIC8vcmVzb3VyY2UKICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5kZXBNYXBzLnB1c2gobW9kdWxlTWFwKTsKCiAgICAgICAgICAgICAgICAgICAgICAgIC8vU3VwcG9ydCBhbm9ueW1vdXMgbW9kdWxlcy4KICAgICAgICAgICAgICAgICAgICAgICAgY29udGV4dC5jb21wbGV0ZUxvYWQobW9kdWxlTmFtZSk7CgogICAgICAgICAgICAgICAgICAgICAgICAvL0JpbmQgdGhlIHZhbHVlIG9mIHRoYXQgbW9kdWxlIHRvIHRoZSB2YWx1ZSBmb3IgdGhpcwogICAgICAgICAgICAgICAgICAgICAgICAvL3Jlc291cmNlIElELgogICAgICAgICAgICAgICAgICAgICAgICBsb2NhbFJlcXVpcmUoW21vZHVsZU5hbWVdLCBsb2FkKTsKICAgICAgICAgICAgICAgICAgICB9KTsKCiAgICAgICAgICAgICAgICAgICAgLy9Vc2UgcGFyZW50TmFtZSBoZXJlIHNpbmNlIHRoZSBwbHVnaW4ncyBuYW1lIGlzIG5vdCByZWxpYWJsZSwKICAgICAgICAgICAgICAgICAgICAvL2NvdWxkIGJlIHNvbWUgd2VpcmQgc3RyaW5nIHdpdGggbm8gcGF0aCB0aGF0IGFjdHVhbGx5IHdhbnRzIHRvCiAgICAgICAgICAgICAgICAgICAgLy9yZWZlcmVuY2UgdGhlIHBhcmVudE5hbWUncyBwYXRoLgogICAgICAgICAgICAgICAgICAgIHBsdWdpbi5sb2FkKG1hcC5uYW1lLCBsb2NhbFJlcXVpcmUsIGxvYWQsIGNvbmZpZyk7CiAgICAgICAgICAgICAgICB9KSk7CgogICAgICAgICAgICAgICAgY29udGV4dC5lbmFibGUocGx1Z2luTWFwLCB0aGlzKTsKICAgICAgICAgICAgICAgIHRoaXMucGx1Z2luTWFwc1twbHVnaW5NYXAuaWRdID0gcGx1Z2luTWFwOwogICAgICAgICAgICB9LAoKICAgICAgICAgICAgZW5hYmxlOiBmdW5jdGlvbiAoKSB7CiAgICAgICAgICAgICAgICBlbmFibGVkUmVnaXN0cnlbdGhpcy5tYXAuaWRdID0gdGhpczsKICAgICAgICAgICAgICAgIHRoaXMuZW5hYmxlZCA9IHRydWU7CgogICAgICAgICAgICAgICAgLy9TZXQgZmxhZyBtZW50aW9uaW5nIHRoYXQgdGhlIG1vZHVsZSBpcyBlbmFibGluZywKICAgICAgICAgICAgICAgIC8vc28gdGhhdCBpbW1lZGlhdGUgY2FsbHMgdG8gdGhlIGRlZmluZWQgY2FsbGJhY2tzCiAgICAgICAgICAgICAgICAvL2ZvciBkZXBlbmRlbmNpZXMgZG8gbm90IHRyaWdnZXIgaW5hZHZlcnRlbnQgbG9hZAogICAgICAgICAgICAgICAgLy93aXRoIHRoZSBkZXBDb3VudCBzdGlsbCBiZWluZyB6ZXJvLgogICAgICAgICAgICAgICAgdGhpcy5lbmFibGluZyA9IHRydWU7CgogICAgICAgICAgICAgICAgLy9FbmFibGUgZWFjaCBkZXBlbmRlbmN5CiAgICAgICAgICAgICAgICBlYWNoKHRoaXMuZGVwTWFwcywgYmluZCh0aGlzLCBmdW5jdGlvbiAoZGVwTWFwLCBpKSB7CiAgICAgICAgICAgICAgICAgICAgdmFyIGlkLCBtb2QsIGhhbmRsZXI7CgogICAgICAgICAgICAgICAgICAgIGlmICh0eXBlb2YgZGVwTWFwID09PSAnc3RyaW5nJykgewogICAgICAgICAgICAgICAgICAgICAgICAvL0RlcGVuZGVuY3kgbmVlZHMgdG8gYmUgY29udmVydGVkIHRvIGEgZGVwTWFwCiAgICAgICAgICAgICAgICAgICAgICAgIC8vYW5kIHdpcmVkIHVwIHRvIHRoaXMgbW9kdWxlLgogICAgICAgICAgICAgICAgICAgICAgICBkZXBNYXAgPSBtYWtlTW9kdWxlTWFwKGRlcE1hcCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAodGhpcy5tYXAuaXNEZWZpbmUgPyB0aGlzLm1hcCA6IHRoaXMubWFwLnBhcmVudE1hcCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFsc2UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIXRoaXMuc2tpcE1hcCk7CiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuZGVwTWFwc1tpXSA9IGRlcE1hcDsKCiAgICAgICAgICAgICAgICAgICAgICAgIGhhbmRsZXIgPSBnZXRPd24oaGFuZGxlcnMsIGRlcE1hcC5pZCk7CgogICAgICAgICAgICAgICAgICAgICAgICBpZiAoaGFuZGxlcikgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5kZXBFeHBvcnRzW2ldID0gaGFuZGxlcih0aGlzKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybjsKICAgICAgICAgICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5kZXBDb3VudCArPSAxOwoKICAgICAgICAgICAgICAgICAgICAgICAgb24oZGVwTWFwLCAnZGVmaW5lZCcsIGJpbmQodGhpcywgZnVuY3Rpb24gKGRlcEV4cG9ydHMpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmICh0aGlzLnVuZGVmZWQpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm47CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmRlZmluZURlcChpLCBkZXBFeHBvcnRzKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuY2hlY2soKTsKICAgICAgICAgICAgICAgICAgICAgICAgfSkpOwoKICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHRoaXMuZXJyYmFjaykgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgb24oZGVwTWFwLCAnZXJyb3InLCBiaW5kKHRoaXMsIHRoaXMuZXJyYmFjaykpOwogICAgICAgICAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKHRoaXMuZXZlbnRzLmVycm9yKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBObyBkaXJlY3QgZXJyYmFjayBvbiB0aGlzIG1vZHVsZSwgYnV0IHNvbWV0aGluZwogICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gZWxzZSBpcyBsaXN0ZW5pbmcgZm9yIGVycm9ycywgc28gYmUgc3VyZSB0bwogICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gcHJvcGFnYXRlIHRoZSBlcnJvciBjb3JyZWN0bHkuCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBvbihkZXBNYXAsICdlcnJvcicsIGJpbmQodGhpcywgZnVuY3Rpb24oZXJyKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5lbWl0KCdlcnJvcicsIGVycik7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9KSk7CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgICAgIGlkID0gZGVwTWFwLmlkOwogICAgICAgICAgICAgICAgICAgIG1vZCA9IHJlZ2lzdHJ5W2lkXTsKCiAgICAgICAgICAgICAgICAgICAgLy9Ta2lwIHNwZWNpYWwgbW9kdWxlcyBsaWtlICdyZXF1aXJlJywgJ2V4cG9ydHMnLCAnbW9kdWxlJwogICAgICAgICAgICAgICAgICAgIC8vQWxzbywgZG9uJ3QgY2FsbCBlbmFibGUgaWYgaXQgaXMgYWxyZWFkeSBlbmFibGVkLAogICAgICAgICAgICAgICAgICAgIC8vaW1wb3J0YW50IGluIGNpcmN1bGFyIGRlcGVuZGVuY3kgY2FzZXMuCiAgICAgICAgICAgICAgICAgICAgaWYgKCFoYXNQcm9wKGhhbmRsZXJzLCBpZCkgJiYgbW9kICYmICFtb2QuZW5hYmxlZCkgewogICAgICAgICAgICAgICAgICAgICAgICBjb250ZXh0LmVuYWJsZShkZXBNYXAsIHRoaXMpOwogICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIH0pKTsKCiAgICAgICAgICAgICAgICAvL0VuYWJsZSBlYWNoIHBsdWdpbiB0aGF0IGlzIHVzZWQgaW4KICAgICAgICAgICAgICAgIC8vYSBkZXBlbmRlbmN5CiAgICAgICAgICAgICAgICBlYWNoUHJvcCh0aGlzLnBsdWdpbk1hcHMsIGJpbmQodGhpcywgZnVuY3Rpb24gKHBsdWdpbk1hcCkgewogICAgICAgICAgICAgICAgICAgIHZhciBtb2QgPSBnZXRPd24ocmVnaXN0cnksIHBsdWdpbk1hcC5pZCk7CiAgICAgICAgICAgICAgICAgICAgaWYgKG1vZCAmJiAhbW9kLmVuYWJsZWQpIHsKICAgICAgICAgICAgICAgICAgICAgICAgY29udGV4dC5lbmFibGUocGx1Z2luTWFwLCB0aGlzKTsKICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICB9KSk7CgogICAgICAgICAgICAgICAgdGhpcy5lbmFibGluZyA9IGZhbHNlOwoKICAgICAgICAgICAgICAgIHRoaXMuY2hlY2soKTsKICAgICAgICAgICAgfSwKCiAgICAgICAgICAgIG9uOiBmdW5jdGlvbiAobmFtZSwgY2IpIHsKICAgICAgICAgICAgICAgIHZhciBjYnMgPSB0aGlzLmV2ZW50c1tuYW1lXTsKICAgICAgICAgICAgICAgIGlmICghY2JzKSB7CiAgICAgICAgICAgICAgICAgICAgY2JzID0gdGhpcy5ldmVudHNbbmFtZV0gPSBbXTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIGNicy5wdXNoKGNiKTsKICAgICAgICAgICAgfSwKCiAgICAgICAgICAgIGVtaXQ6IGZ1bmN0aW9uIChuYW1lLCBldnQpIHsKICAgICAgICAgICAgICAgIGVhY2godGhpcy5ldmVudHNbbmFtZV0sIGZ1bmN0aW9uIChjYikgewogICAgICAgICAgICAgICAgICAgIGNiKGV2dCk7CiAgICAgICAgICAgICAgICB9KTsKICAgICAgICAgICAgICAgIGlmIChuYW1lID09PSAnZXJyb3InKSB7CiAgICAgICAgICAgICAgICAgICAgLy9Ob3cgdGhhdCB0aGUgZXJyb3IgaGFuZGxlciB3YXMgdHJpZ2dlcmVkLCByZW1vdmUKICAgICAgICAgICAgICAgICAgICAvL3RoZSBsaXN0ZW5lcnMsIHNpbmNlIHRoaXMgYnJva2VuIE1vZHVsZSBpbnN0YW5jZQogICAgICAgICAgICAgICAgICAgIC8vY2FuIHN0YXkgYXJvdW5kIGZvciBhIHdoaWxlIGluIHRoZSByZWdpc3RyeS4KICAgICAgICAgICAgICAgICAgICBkZWxldGUgdGhpcy5ldmVudHNbbmFtZV07CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICB9OwoKICAgICAgICBmdW5jdGlvbiBjYWxsR2V0TW9kdWxlKGFyZ3MpIHsKICAgICAgICAgICAgLy9Ta2lwIG1vZHVsZXMgYWxyZWFkeSBkZWZpbmVkLgogICAgICAgICAgICBpZiAoIWhhc1Byb3AoZGVmaW5lZCwgYXJnc1swXSkpIHsKICAgICAgICAgICAgICAgIGdldE1vZHVsZShtYWtlTW9kdWxlTWFwKGFyZ3NbMF0sIG51bGwsIHRydWUpKS5pbml0KGFyZ3NbMV0sIGFyZ3NbMl0pOwogICAgICAgICAgICB9CiAgICAgICAgfQoKICAgICAgICBmdW5jdGlvbiByZW1vdmVMaXN0ZW5lcihub2RlLCBmdW5jLCBuYW1lLCBpZU5hbWUpIHsKICAgICAgICAgICAgLy9GYXZvciBkZXRhY2hFdmVudCBiZWNhdXNlIG9mIElFOQogICAgICAgICAgICAvL2lzc3VlLCBzZWUgYXR0YWNoRXZlbnQvYWRkRXZlbnRMaXN0ZW5lciBjb21tZW50IGVsc2V3aGVyZQogICAgICAgICAgICAvL2luIHRoaXMgZmlsZS4KICAgICAgICAgICAgaWYgKG5vZGUuZGV0YWNoRXZlbnQgJiYgIWlzT3BlcmEpIHsKICAgICAgICAgICAgICAgIC8vUHJvYmFibHkgSUUuIElmIG5vdCBpdCB3aWxsIHRocm93IGFuIGVycm9yLCB3aGljaCB3aWxsIGJlCiAgICAgICAgICAgICAgICAvL3VzZWZ1bCB0byBrbm93LgogICAgICAgICAgICAgICAgaWYgKGllTmFtZSkgewogICAgICAgICAgICAgICAgICAgIG5vZGUuZGV0YWNoRXZlbnQoaWVOYW1lLCBmdW5jKTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgICAgIG5vZGUucmVtb3ZlRXZlbnRMaXN0ZW5lcihuYW1lLCBmdW5jLCBmYWxzZSk7CiAgICAgICAgICAgIH0KICAgICAgICB9CgogICAgICAgIC8qKgogICAgICAgICAqIEdpdmVuIGFuIGV2ZW50IGZyb20gYSBzY3JpcHQgbm9kZSwgZ2V0IHRoZSByZXF1aXJlanMgaW5mbyBmcm9tIGl0LAogICAgICAgICAqIGFuZCB0aGVuIHJlbW92ZXMgdGhlIGV2ZW50IGxpc3RlbmVycyBvbiB0aGUgbm9kZS4KICAgICAgICAgKiBAcGFyYW0ge0V2ZW50fSBldnQKICAgICAgICAgKiBAcmV0dXJucyB7T2JqZWN0fQogICAgICAgICAqLwogICAgICAgIGZ1bmN0aW9uIGdldFNjcmlwdERhdGEoZXZ0KSB7CiAgICAgICAgICAgIC8vVXNpbmcgY3VycmVudFRhcmdldCBpbnN0ZWFkIG9mIHRhcmdldCBmb3IgRmlyZWZveCAyLjAncyBzYWtlLiBOb3QKICAgICAgICAgICAgLy9hbGwgb2xkIGJyb3dzZXJzIHdpbGwgYmUgc3VwcG9ydGVkLCBidXQgdGhpcyBvbmUgd2FzIGVhc3kgZW5vdWdoCiAgICAgICAgICAgIC8vdG8gc3VwcG9ydCBhbmQgc3RpbGwgbWFrZXMgc2Vuc2UuCiAgICAgICAgICAgIHZhciBub2RlID0gZXZ0LmN1cnJlbnRUYXJnZXQgfHwgZXZ0LnNyY0VsZW1lbnQ7CgogICAgICAgICAgICAvL1JlbW92ZSB0aGUgbGlzdGVuZXJzIG9uY2UgaGVyZS4KICAgICAgICAgICAgcmVtb3ZlTGlzdGVuZXIobm9kZSwgY29udGV4dC5vblNjcmlwdExvYWQsICdsb2FkJywgJ29ucmVhZHlzdGF0ZWNoYW5nZScpOwogICAgICAgICAgICByZW1vdmVMaXN0ZW5lcihub2RlLCBjb250ZXh0Lm9uU2NyaXB0RXJyb3IsICdlcnJvcicpOwoKICAgICAgICAgICAgcmV0dXJuIHsKICAgICAgICAgICAgICAgIG5vZGU6IG5vZGUsCiAgICAgICAgICAgICAgICBpZDogbm9kZSAmJiBub2RlLmdldEF0dHJpYnV0ZSgnZGF0YS1yZXF1aXJlbW9kdWxlJykKICAgICAgICAgICAgfTsKICAgICAgICB9CgogICAgICAgIGZ1bmN0aW9uIGludGFrZURlZmluZXMoKSB7CiAgICAgICAgICAgIHZhciBhcmdzOwoKICAgICAgICAgICAgLy9BbnkgZGVmaW5lZCBtb2R1bGVzIGluIHRoZSBnbG9iYWwgcXVldWUsIGludGFrZSB0aGVtIG5vdy4KICAgICAgICAgICAgdGFrZUdsb2JhbFF1ZXVlKCk7CgogICAgICAgICAgICAvL01ha2Ugc3VyZSBhbnkgcmVtYWluaW5nIGRlZlF1ZXVlIGl0ZW1zIGdldCBwcm9wZXJseSBwcm9jZXNzZWQuCiAgICAgICAgICAgIHdoaWxlIChkZWZRdWV1ZS5sZW5ndGgpIHsKICAgICAgICAgICAgICAgIGFyZ3MgPSBkZWZRdWV1ZS5zaGlmdCgpOwogICAgICAgICAgICAgICAgaWYgKGFyZ3NbMF0gPT09IG51bGwpIHsKICAgICAgICAgICAgICAgICAgICByZXR1cm4gb25FcnJvcihtYWtlRXJyb3IoJ21pc21hdGNoJywgJ01pc21hdGNoZWQgYW5vbnltb3VzIGRlZmluZSgpIG1vZHVsZTogJyArCiAgICAgICAgICAgICAgICAgICAgICAgIGFyZ3NbYXJncy5sZW5ndGggLSAxXSkpOwogICAgICAgICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgICAgICAgICAvL2FyZ3MgYXJlIGlkLCBkZXBzLCBmYWN0b3J5LiBTaG91bGQgYmUgbm9ybWFsaXplZCBieSB0aGUKICAgICAgICAgICAgICAgICAgICAvL2RlZmluZSgpIGZ1bmN0aW9uLgogICAgICAgICAgICAgICAgICAgIGNhbGxHZXRNb2R1bGUoYXJncyk7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICAgICAgY29udGV4dC5kZWZRdWV1ZU1hcCA9IHt9OwogICAgICAgIH0KCiAgICAgICAgY29udGV4dCA9IHsKICAgICAgICAgICAgY29uZmlnOiBjb25maWcsCiAgICAgICAgICAgIGNvbnRleHROYW1lOiBjb250ZXh0TmFtZSwKICAgICAgICAgICAgcmVnaXN0cnk6IHJlZ2lzdHJ5LAogICAgICAgICAgICBkZWZpbmVkOiBkZWZpbmVkLAogICAgICAgICAgICB1cmxGZXRjaGVkOiB1cmxGZXRjaGVkLAogICAgICAgICAgICBkZWZRdWV1ZTogZGVmUXVldWUsCiAgICAgICAgICAgIGRlZlF1ZXVlTWFwOiB7fSwKICAgICAgICAgICAgTW9kdWxlOiBNb2R1bGUsCiAgICAgICAgICAgIG1ha2VNb2R1bGVNYXA6IG1ha2VNb2R1bGVNYXAsCiAgICAgICAgICAgIG5leHRUaWNrOiByZXEubmV4dFRpY2ssCiAgICAgICAgICAgIG9uRXJyb3I6IG9uRXJyb3IsCgogICAgICAgICAgICAvKioKICAgICAgICAgICAgICogU2V0IGEgY29uZmlndXJhdGlvbiBmb3IgdGhlIGNvbnRleHQuCiAgICAgICAgICAgICAqIEBwYXJhbSB7T2JqZWN0fSBjZmcgY29uZmlnIG9iamVjdCB0byBpbnRlZ3JhdGUuCiAgICAgICAgICAgICAqLwogICAgICAgICAgICBjb25maWd1cmU6IGZ1bmN0aW9uIChjZmcpIHsKICAgICAgICAgICAgICAgIC8vTWFrZSBzdXJlIHRoZSBiYXNlVXJsIGVuZHMgaW4gYSBzbGFzaC4KICAgICAgICAgICAgICAgIGlmIChjZmcuYmFzZVVybCkgewogICAgICAgICAgICAgICAgICAgIGlmIChjZmcuYmFzZVVybC5jaGFyQXQoY2ZnLmJhc2VVcmwubGVuZ3RoIC0gMSkgIT09ICcvJykgewogICAgICAgICAgICAgICAgICAgICAgICBjZmcuYmFzZVVybCArPSAnLyc7CiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgIC8vU2F2ZSBvZmYgdGhlIHBhdGhzIHNpbmNlIHRoZXkgcmVxdWlyZSBzcGVjaWFsIHByb2Nlc3NpbmcsCiAgICAgICAgICAgICAgICAvL3RoZXkgYXJlIGFkZGl0aXZlLgogICAgICAgICAgICAgICAgdmFyIHNoaW0gPSBjb25maWcuc2hpbSwKICAgICAgICAgICAgICAgICAgICBvYmpzID0gewogICAgICAgICAgICAgICAgICAgICAgICBwYXRoczogdHJ1ZSwKICAgICAgICAgICAgICAgICAgICAgICAgYnVuZGxlczogdHJ1ZSwKICAgICAgICAgICAgICAgICAgICAgICAgY29uZmlnOiB0cnVlLAogICAgICAgICAgICAgICAgICAgICAgICBtYXA6IHRydWUKICAgICAgICAgICAgICAgICAgICB9OwoKICAgICAgICAgICAgICAgIGVhY2hQcm9wKGNmZywgZnVuY3Rpb24gKHZhbHVlLCBwcm9wKSB7CiAgICAgICAgICAgICAgICAgICAgaWYgKG9ianNbcHJvcF0pIHsKICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCFjb25maWdbcHJvcF0pIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbmZpZ1twcm9wXSA9IHt9OwogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIG1peGluKGNvbmZpZ1twcm9wXSwgdmFsdWUsIHRydWUsIHRydWUpOwogICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgICAgICAgICAgICAgIGNvbmZpZ1twcm9wXSA9IHZhbHVlOwogICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIH0pOwoKICAgICAgICAgICAgICAgIC8vUmV2ZXJzZSBtYXAgdGhlIGJ1bmRsZXMKICAgICAgICAgICAgICAgIGlmIChjZmcuYnVuZGxlcykgewogICAgICAgICAgICAgICAgICAgIGVhY2hQcm9wKGNmZy5idW5kbGVzLCBmdW5jdGlvbiAodmFsdWUsIHByb3ApIHsKICAgICAgICAgICAgICAgICAgICAgICAgZWFjaCh2YWx1ZSwgZnVuY3Rpb24gKHYpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmICh2ICE9PSBwcm9wKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnVuZGxlc01hcFt2XSA9IHByb3A7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0pOwogICAgICAgICAgICAgICAgICAgIH0pOwogICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgIC8vTWVyZ2Ugc2hpbQogICAgICAgICAgICAgICAgaWYgKGNmZy5zaGltKSB7CiAgICAgICAgICAgICAgICAgICAgZWFjaFByb3AoY2ZnLnNoaW0sIGZ1bmN0aW9uICh2YWx1ZSwgaWQpIHsKICAgICAgICAgICAgICAgICAgICAgICAgLy9Ob3JtYWxpemUgdGhlIHN0cnVjdHVyZQogICAgICAgICAgICAgICAgICAgICAgICBpZiAoaXNBcnJheSh2YWx1ZSkpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlID0gewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlcHM6IHZhbHVlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9OwogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIGlmICgodmFsdWUuZXhwb3J0cyB8fCB2YWx1ZS5pbml0KSAmJiAhdmFsdWUuZXhwb3J0c0ZuKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZS5leHBvcnRzRm4gPSBjb250ZXh0Lm1ha2VTaGltRXhwb3J0cyh2YWx1ZSk7CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgc2hpbVtpZF0gPSB2YWx1ZTsKICAgICAgICAgICAgICAgICAgICB9KTsKICAgICAgICAgICAgICAgICAgICBjb25maWcuc2hpbSA9IHNoaW07CiAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgLy9BZGp1c3QgcGFja2FnZXMgaWYgbmVjZXNzYXJ5LgogICAgICAgICAgICAgICAgaWYgKGNmZy5wYWNrYWdlcykgewogICAgICAgICAgICAgICAgICAgIGVhY2goY2ZnLnBhY2thZ2VzLCBmdW5jdGlvbiAocGtnT2JqKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIHZhciBsb2NhdGlvbiwgbmFtZTsKCiAgICAgICAgICAgICAgICAgICAgICAgIHBrZ09iaiA9IHR5cGVvZiBwa2dPYmogPT09ICdzdHJpbmcnID8ge25hbWU6IHBrZ09ian0gOiBwa2dPYmo7CgogICAgICAgICAgICAgICAgICAgICAgICBuYW1lID0gcGtnT2JqLm5hbWU7CiAgICAgICAgICAgICAgICAgICAgICAgIGxvY2F0aW9uID0gcGtnT2JqLmxvY2F0aW9uOwogICAgICAgICAgICAgICAgICAgICAgICBpZiAobG9jYXRpb24pIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbmZpZy5wYXRoc1tuYW1lXSA9IHBrZ09iai5sb2NhdGlvbjsKICAgICAgICAgICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgICAgICAgICAgLy9TYXZlIHBvaW50ZXIgdG8gbWFpbiBtb2R1bGUgSUQgZm9yIHBrZyBuYW1lLgogICAgICAgICAgICAgICAgICAgICAgICAvL1JlbW92ZSBsZWFkaW5nIGRvdCBpbiBtYWluLCBzbyBtYWluIHBhdGhzIGFyZSBub3JtYWxpemVkLAogICAgICAgICAgICAgICAgICAgICAgICAvL2FuZCByZW1vdmUgYW55IHRyYWlsaW5nIC5qcywgc2luY2UgZGlmZmVyZW50IHBhY2thZ2UKICAgICAgICAgICAgICAgICAgICAgICAgLy9lbnZzIGhhdmUgZGlmZmVyZW50IGNvbnZlbnRpb25zOiBzb21lIHVzZSBhIG1vZHVsZSBuYW1lLAogICAgICAgICAgICAgICAgICAgICAgICAvL3NvbWUgdXNlIGEgZmlsZSBuYW1lLgogICAgICAgICAgICAgICAgICAgICAgICBjb25maWcucGtnc1tuYW1lXSA9IHBrZ09iai5uYW1lICsgJy8nICsgKHBrZ09iai5tYWluIHx8ICdtYWluJykKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC5yZXBsYWNlKGN1cnJEaXJSZWdFeHAsICcnKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLnJlcGxhY2UoanNTdWZmaXhSZWdFeHAsICcnKTsKICAgICAgICAgICAgICAgICAgICB9KTsKICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICAvL0lmIHRoZXJlIGFyZSBhbnkgIndhaXRpbmcgdG8gZXhlY3V0ZSIgbW9kdWxlcyBpbiB0aGUgcmVnaXN0cnksCiAgICAgICAgICAgICAgICAvL3VwZGF0ZSB0aGUgbWFwcyBmb3IgdGhlbSwgc2luY2UgdGhlaXIgaW5mbywgbGlrZSBVUkxzIHRvIGxvYWQsCiAgICAgICAgICAgICAgICAvL21heSBoYXZlIGNoYW5nZWQuCiAgICAgICAgICAgICAgICBlYWNoUHJvcChyZWdpc3RyeSwgZnVuY3Rpb24gKG1vZCwgaWQpIHsKICAgICAgICAgICAgICAgICAgICAvL0lmIG1vZHVsZSBhbHJlYWR5IGhhcyBpbml0IGNhbGxlZCwgc2luY2UgaXQgaXMgdG9vCiAgICAgICAgICAgICAgICAgICAgLy9sYXRlIHRvIG1vZGlmeSB0aGVtLCBhbmQgaWdub3JlIHVubm9ybWFsaXplZCBvbmVzCiAgICAgICAgICAgICAgICAgICAgLy9zaW5jZSB0aGV5IGFyZSB0cmFuc2llbnQuCiAgICAgICAgICAgICAgICAgICAgaWYgKCFtb2QuaW5pdGVkICYmICFtb2QubWFwLnVubm9ybWFsaXplZCkgewogICAgICAgICAgICAgICAgICAgICAgICBtb2QubWFwID0gbWFrZU1vZHVsZU1hcChpZCwgbnVsbCwgdHJ1ZSk7CiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfSk7CgogICAgICAgICAgICAgICAgLy9JZiBhIGRlcHMgYXJyYXkgb3IgYSBjb25maWcgY2FsbGJhY2sgaXMgc3BlY2lmaWVkLCB0aGVuIGNhbGwKICAgICAgICAgICAgICAgIC8vcmVxdWlyZSB3aXRoIHRob3NlIGFyZ3MuIFRoaXMgaXMgdXNlZnVsIHdoZW4gcmVxdWlyZSBpcyBkZWZpbmVkIGFzIGEKICAgICAgICAgICAgICAgIC8vY29uZmlnIG9iamVjdCBiZWZvcmUgcmVxdWlyZS5qcyBpcyBsb2FkZWQuCiAgICAgICAgICAgICAgICBpZiAoY2ZnLmRlcHMgfHwgY2ZnLmNhbGxiYWNrKSB7CiAgICAgICAgICAgICAgICAgICAgY29udGV4dC5yZXF1aXJlKGNmZy5kZXBzIHx8IFtdLCBjZmcuY2FsbGJhY2spOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAoKICAgICAgICAgICAgbWFrZVNoaW1FeHBvcnRzOiBmdW5jdGlvbiAodmFsdWUpIHsKICAgICAgICAgICAgICAgIGZ1bmN0aW9uIGZuKCkgewogICAgICAgICAgICAgICAgICAgIHZhciByZXQ7CiAgICAgICAgICAgICAgICAgICAgaWYgKHZhbHVlLmluaXQpIHsKICAgICAgICAgICAgICAgICAgICAgICAgcmV0ID0gdmFsdWUuaW5pdC5hcHBseShnbG9iYWwsIGFyZ3VtZW50cyk7CiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgIHJldHVybiByZXQgfHwgKHZhbHVlLmV4cG9ydHMgJiYgZ2V0R2xvYmFsKHZhbHVlLmV4cG9ydHMpKTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIHJldHVybiBmbjsKICAgICAgICAgICAgfSwKCiAgICAgICAgICAgIG1ha2VSZXF1aXJlOiBmdW5jdGlvbiAocmVsTWFwLCBvcHRpb25zKSB7CiAgICAgICAgICAgICAgICBvcHRpb25zID0gb3B0aW9ucyB8fCB7fTsKCiAgICAgICAgICAgICAgICBmdW5jdGlvbiBsb2NhbFJlcXVpcmUoZGVwcywgY2FsbGJhY2ssIGVycmJhY2spIHsKICAgICAgICAgICAgICAgICAgICB2YXIgaWQsIG1hcCwgcmVxdWlyZU1vZDsKCiAgICAgICAgICAgICAgICAgICAgaWYgKG9wdGlvbnMuZW5hYmxlQnVpbGRDYWxsYmFjayAmJiBjYWxsYmFjayAmJiBpc0Z1bmN0aW9uKGNhbGxiYWNrKSkgewogICAgICAgICAgICAgICAgICAgICAgICBjYWxsYmFjay5fX3JlcXVpcmVKc0J1aWxkID0gdHJ1ZTsKICAgICAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgICAgIGlmICh0eXBlb2YgZGVwcyA9PT0gJ3N0cmluZycpIHsKICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGlzRnVuY3Rpb24oY2FsbGJhY2spKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvL0ludmFsaWQgY2FsbAogICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIG9uRXJyb3IobWFrZUVycm9yKCdyZXF1aXJlYXJncycsICdJbnZhbGlkIHJlcXVpcmUgY2FsbCcpLCBlcnJiYWNrKTsKICAgICAgICAgICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgICAgICAgICAgLy9JZiByZXF1aXJlfGV4cG9ydHN8bW9kdWxlIGFyZSByZXF1ZXN0ZWQsIGdldCB0aGUKICAgICAgICAgICAgICAgICAgICAgICAgLy92YWx1ZSBmb3IgdGhlbSBmcm9tIHRoZSBzcGVjaWFsIGhhbmRsZXJzLiBDYXZlYXQ6CiAgICAgICAgICAgICAgICAgICAgICAgIC8vdGhpcyBvbmx5IHdvcmtzIHdoaWxlIG1vZHVsZSBpcyBiZWluZyBkZWZpbmVkLgogICAgICAgICAgICAgICAgICAgICAgICBpZiAocmVsTWFwICYmIGhhc1Byb3AoaGFuZGxlcnMsIGRlcHMpKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gaGFuZGxlcnNbZGVwc10ocmVnaXN0cnlbcmVsTWFwLmlkXSk7CiAgICAgICAgICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICAgICAgICAgIC8vU3luY2hyb25vdXMgYWNjZXNzIHRvIG9uZSBtb2R1bGUuIElmIHJlcXVpcmUuZ2V0IGlzCiAgICAgICAgICAgICAgICAgICAgICAgIC8vYXZhaWxhYmxlIChhcyBpbiB0aGUgTm9kZSBhZGFwdGVyKSwgcHJlZmVyIHRoYXQuCiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChyZXEuZ2V0KSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gcmVxLmdldChjb250ZXh0LCBkZXBzLCByZWxNYXAsIGxvY2FsUmVxdWlyZSk7CiAgICAgICAgICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICAgICAgICAgIC8vTm9ybWFsaXplIG1vZHVsZSBuYW1lLCBpZiBpdCBjb250YWlucyAuIG9yIC4uCiAgICAgICAgICAgICAgICAgICAgICAgIG1hcCA9IG1ha2VNb2R1bGVNYXAoZGVwcywgcmVsTWFwLCBmYWxzZSwgdHJ1ZSk7CiAgICAgICAgICAgICAgICAgICAgICAgIGlkID0gbWFwLmlkOwoKICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCFoYXNQcm9wKGRlZmluZWQsIGlkKSkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIG9uRXJyb3IobWFrZUVycm9yKCdub3Rsb2FkZWQnLCAnTW9kdWxlIG5hbWUgIicgKwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWQgKwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJyIgaGFzIG5vdCBiZWVuIGxvYWRlZCB5ZXQgZm9yIGNvbnRleHQ6ICcgKwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29udGV4dE5hbWUgKwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKHJlbE1hcCA/ICcnIDogJy4gVXNlIHJlcXVpcmUoW10pJykpKTsKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gZGVmaW5lZFtpZF07CiAgICAgICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgICAgICAvL0dyYWIgZGVmaW5lcyB3YWl0aW5nIGluIHRoZSBnbG9iYWwgcXVldWUuCiAgICAgICAgICAgICAgICAgICAgaW50YWtlRGVmaW5lcygpOwoKICAgICAgICAgICAgICAgICAgICAvL01hcmsgYWxsIHRoZSBkZXBlbmRlbmNpZXMgYXMgbmVlZGluZyB0byBiZSBsb2FkZWQuCiAgICAgICAgICAgICAgICAgICAgY29udGV4dC5uZXh0VGljayhmdW5jdGlvbiAoKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIC8vU29tZSBkZWZpbmVzIGNvdWxkIGhhdmUgYmVlbiBhZGRlZCBzaW5jZSB0aGUKICAgICAgICAgICAgICAgICAgICAgICAgLy9yZXF1aXJlIGNhbGwsIGNvbGxlY3QgdGhlbS4KICAgICAgICAgICAgICAgICAgICAgICAgaW50YWtlRGVmaW5lcygpOwoKICAgICAgICAgICAgICAgICAgICAgICAgcmVxdWlyZU1vZCA9IGdldE1vZHVsZShtYWtlTW9kdWxlTWFwKG51bGwsIHJlbE1hcCkpOwoKICAgICAgICAgICAgICAgICAgICAgICAgLy9TdG9yZSBpZiBtYXAgY29uZmlnIHNob3VsZCBiZSBhcHBsaWVkIHRvIHRoaXMgcmVxdWlyZQogICAgICAgICAgICAgICAgICAgICAgICAvL2NhbGwgZm9yIGRlcGVuZGVuY2llcy4KICAgICAgICAgICAgICAgICAgICAgICAgcmVxdWlyZU1vZC5za2lwTWFwID0gb3B0aW9ucy5za2lwTWFwOwoKICAgICAgICAgICAgICAgICAgICAgICAgcmVxdWlyZU1vZC5pbml0KGRlcHMsIGNhbGxiYWNrLCBlcnJiYWNrLCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbmFibGVkOiB0cnVlCiAgICAgICAgICAgICAgICAgICAgICAgIH0pOwoKICAgICAgICAgICAgICAgICAgICAgICAgY2hlY2tMb2FkZWQoKTsKICAgICAgICAgICAgICAgICAgICB9KTsKCiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGxvY2FsUmVxdWlyZTsKICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICBtaXhpbihsb2NhbFJlcXVpcmUsIHsKICAgICAgICAgICAgICAgICAgICBpc0Jyb3dzZXI6IGlzQnJvd3NlciwKCiAgICAgICAgICAgICAgICAgICAgLyoqCiAgICAgICAgICAgICAgICAgICAgICogQ29udmVydHMgYSBtb2R1bGUgbmFtZSArIC5leHRlbnNpb24gaW50byBhbiBVUkwgcGF0aC4KICAgICAgICAgICAgICAgICAgICAgKiAqUmVxdWlyZXMqIHRoZSB1c2Ugb2YgYSBtb2R1bGUgbmFtZS4gSXQgZG9lcyBub3Qgc3VwcG9ydCB1c2luZwogICAgICAgICAgICAgICAgICAgICAqIHBsYWluIFVSTHMgbGlrZSBuYW1lVG9VcmwuCiAgICAgICAgICAgICAgICAgICAgICovCiAgICAgICAgICAgICAgICAgICAgdG9Vcmw6IGZ1bmN0aW9uIChtb2R1bGVOYW1lUGx1c0V4dCkgewogICAgICAgICAgICAgICAgICAgICAgICB2YXIgZXh0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5kZXggPSBtb2R1bGVOYW1lUGx1c0V4dC5sYXN0SW5kZXhPZignLicpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VnbWVudCA9IG1vZHVsZU5hbWVQbHVzRXh0LnNwbGl0KCcvJylbMF0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpc1JlbGF0aXZlID0gc2VnbWVudCA9PT0gJy4nIHx8IHNlZ21lbnQgPT09ICcuLic7CgogICAgICAgICAgICAgICAgICAgICAgICAvL0hhdmUgYSBmaWxlIGV4dGVuc2lvbiBhbGlhcywgYW5kIGl0IGlzIG5vdCB0aGUKICAgICAgICAgICAgICAgICAgICAgICAgLy9kb3RzIGZyb20gYSByZWxhdGl2ZSBwYXRoLgogICAgICAgICAgICAgICAgICAgICAgICBpZiAoaW5kZXggIT09IC0xICYmICghaXNSZWxhdGl2ZSB8fCBpbmRleCA+IDEpKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBleHQgPSBtb2R1bGVOYW1lUGx1c0V4dC5zdWJzdHJpbmcoaW5kZXgsIG1vZHVsZU5hbWVQbHVzRXh0Lmxlbmd0aCk7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtb2R1bGVOYW1lUGx1c0V4dCA9IG1vZHVsZU5hbWVQbHVzRXh0LnN1YnN0cmluZygwLCBpbmRleCk7CiAgICAgICAgICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiBjb250ZXh0Lm5hbWVUb1VybChub3JtYWxpemUobW9kdWxlTmFtZVBsdXNFeHQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlbE1hcCAmJiByZWxNYXAuaWQsIHRydWUpLCBleHQsICB0cnVlKTsKICAgICAgICAgICAgICAgICAgICB9LAoKICAgICAgICAgICAgICAgICAgICBkZWZpbmVkOiBmdW5jdGlvbiAoaWQpIHsKICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGhhc1Byb3AoZGVmaW5lZCwgbWFrZU1vZHVsZU1hcChpZCwgcmVsTWFwLCBmYWxzZSwgdHJ1ZSkuaWQpOwogICAgICAgICAgICAgICAgICAgIH0sCgogICAgICAgICAgICAgICAgICAgIHNwZWNpZmllZDogZnVuY3Rpb24gKGlkKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIGlkID0gbWFrZU1vZHVsZU1hcChpZCwgcmVsTWFwLCBmYWxzZSwgdHJ1ZSkuaWQ7CiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiBoYXNQcm9wKGRlZmluZWQsIGlkKSB8fCBoYXNQcm9wKHJlZ2lzdHJ5LCBpZCk7CiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfSk7CgogICAgICAgICAgICAgICAgLy9Pbmx5IGFsbG93IHVuZGVmIG9uIHRvcCBsZXZlbCByZXF1aXJlIGNhbGxzCiAgICAgICAgICAgICAgICBpZiAoIXJlbE1hcCkgewogICAgICAgICAgICAgICAgICAgIGxvY2FsUmVxdWlyZS51bmRlZiA9IGZ1bmN0aW9uIChpZCkgewogICAgICAgICAgICAgICAgICAgICAgICAvL0JpbmQgYW55IHdhaXRpbmcgZGVmaW5lKCkgY2FsbHMgdG8gdGhpcyBjb250ZXh0LAogICAgICAgICAgICAgICAgICAgICAgICAvL2ZpeCBmb3IgIzQwOAogICAgICAgICAgICAgICAgICAgICAgICB0YWtlR2xvYmFsUXVldWUoKTsKCiAgICAgICAgICAgICAgICAgICAgICAgIHZhciBtYXAgPSBtYWtlTW9kdWxlTWFwKGlkLCByZWxNYXAsIHRydWUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9kID0gZ2V0T3duKHJlZ2lzdHJ5LCBpZCk7CgogICAgICAgICAgICAgICAgICAgICAgICBtb2QudW5kZWZlZCA9IHRydWU7CiAgICAgICAgICAgICAgICAgICAgICAgIHJlbW92ZVNjcmlwdChpZCk7CgogICAgICAgICAgICAgICAgICAgICAgICBkZWxldGUgZGVmaW5lZFtpZF07CiAgICAgICAgICAgICAgICAgICAgICAgIGRlbGV0ZSB1cmxGZXRjaGVkW21hcC51cmxdOwogICAgICAgICAgICAgICAgICAgICAgICBkZWxldGUgdW5kZWZFdmVudHNbaWRdOwoKICAgICAgICAgICAgICAgICAgICAgICAgLy9DbGVhbiBxdWV1ZWQgZGVmaW5lcyB0b28uIEdvIGJhY2t3YXJkcwogICAgICAgICAgICAgICAgICAgICAgICAvL2luIGFycmF5IHNvIHRoYXQgdGhlIHNwbGljZXMgZG8gbm90CiAgICAgICAgICAgICAgICAgICAgICAgIC8vbWVzcyB1cCB0aGUgaXRlcmF0aW9uLgogICAgICAgICAgICAgICAgICAgICAgICBlYWNoUmV2ZXJzZShkZWZRdWV1ZSwgZnVuY3Rpb24oYXJncywgaSkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGFyZ3NbMF0gPT09IGlkKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVmUXVldWUuc3BsaWNlKGksIDEpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9KTsKICAgICAgICAgICAgICAgICAgICAgICAgZGVsZXRlIGNvbnRleHQuZGVmUXVldWVNYXBbaWRdOwoKICAgICAgICAgICAgICAgICAgICAgICAgaWYgKG1vZCkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgLy9Ib2xkIG9uIHRvIGxpc3RlbmVycyBpbiBjYXNlIHRoZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgLy9tb2R1bGUgd2lsbCBiZSBhdHRlbXB0ZWQgdG8gYmUgcmVsb2FkZWQKICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vdXNpbmcgYSBkaWZmZXJlbnQgY29uZmlnLgogICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKG1vZC5ldmVudHMuZGVmaW5lZCkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVuZGVmRXZlbnRzW2lkXSA9IG1vZC5ldmVudHM7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgICAgICAgICAgICAgY2xlYW5SZWdpc3RyeShpZCk7CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICB9OwogICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgIHJldHVybiBsb2NhbFJlcXVpcmU7CiAgICAgICAgICAgIH0sCgogICAgICAgICAgICAvKioKICAgICAgICAgICAgICogQ2FsbGVkIHRvIGVuYWJsZSBhIG1vZHVsZSBpZiBpdCBpcyBzdGlsbCBpbiB0aGUgcmVnaXN0cnkKICAgICAgICAgICAgICogYXdhaXRpbmcgZW5hYmxlbWVudC4gQSBzZWNvbmQgYXJnLCBwYXJlbnQsIHRoZSBwYXJlbnQgbW9kdWxlLAogICAgICAgICAgICAgKiBpcyBwYXNzZWQgaW4gZm9yIGNvbnRleHQsIHdoZW4gdGhpcyBtZXRob2QgaXMgb3ZlcnJpZGRlbiBieQogICAgICAgICAgICAgKiB0aGUgb3B0aW1pemVyLiBOb3Qgc2hvd24gaGVyZSB0byBrZWVwIGNvZGUgY29tcGFjdC4KICAgICAgICAgICAgICovCiAgICAgICAgICAgIGVuYWJsZTogZnVuY3Rpb24gKGRlcE1hcCkgewogICAgICAgICAgICAgICAgdmFyIG1vZCA9IGdldE93bihyZWdpc3RyeSwgZGVwTWFwLmlkKTsKICAgICAgICAgICAgICAgIGlmIChtb2QpIHsKICAgICAgICAgICAgICAgICAgICBnZXRNb2R1bGUoZGVwTWFwKS5lbmFibGUoKTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKCiAgICAgICAgICAgIC8qKgogICAgICAgICAgICAgKiBJbnRlcm5hbCBtZXRob2QgdXNlZCBieSBlbnZpcm9ubWVudCBhZGFwdGVycyB0byBjb21wbGV0ZSBhIGxvYWQgZXZlbnQuCiAgICAgICAgICAgICAqIEEgbG9hZCBldmVudCBjb3VsZCBiZSBhIHNjcmlwdCBsb2FkIG9yIGp1c3QgYSBsb2FkIHBhc3MgZnJvbSBhIHN5bmNocm9ub3VzCiAgICAgICAgICAgICAqIGxvYWQgY2FsbC4KICAgICAgICAgICAgICogQHBhcmFtIHtTdHJpbmd9IG1vZHVsZU5hbWUgdGhlIG5hbWUgb2YgdGhlIG1vZHVsZSB0byBwb3RlbnRpYWxseSBjb21wbGV0ZS4KICAgICAgICAgICAgICovCiAgICAgICAgICAgIGNvbXBsZXRlTG9hZDogZnVuY3Rpb24gKG1vZHVsZU5hbWUpIHsKICAgICAgICAgICAgICAgIHZhciBmb3VuZCwgYXJncywgbW9kLAogICAgICAgICAgICAgICAgICAgIHNoaW0gPSBnZXRPd24oY29uZmlnLnNoaW0sIG1vZHVsZU5hbWUpIHx8IHt9LAogICAgICAgICAgICAgICAgICAgIHNoRXhwb3J0cyA9IHNoaW0uZXhwb3J0czsKCiAgICAgICAgICAgICAgICB0YWtlR2xvYmFsUXVldWUoKTsKCiAgICAgICAgICAgICAgICB3aGlsZSAoZGVmUXVldWUubGVuZ3RoKSB7CiAgICAgICAgICAgICAgICAgICAgYXJncyA9IGRlZlF1ZXVlLnNoaWZ0KCk7CiAgICAgICAgICAgICAgICAgICAgaWYgKGFyZ3NbMF0gPT09IG51bGwpIHsKICAgICAgICAgICAgICAgICAgICAgICAgYXJnc1swXSA9IG1vZHVsZU5hbWU7CiAgICAgICAgICAgICAgICAgICAgICAgIC8vSWYgYWxyZWFkeSBmb3VuZCBhbiBhbm9ueW1vdXMgbW9kdWxlIGFuZCBib3VuZCBpdAogICAgICAgICAgICAgICAgICAgICAgICAvL3RvIHRoaXMgbmFtZSwgdGhlbiB0aGlzIGlzIHNvbWUgb3RoZXIgYW5vbiBtb2R1bGUKICAgICAgICAgICAgICAgICAgICAgICAgLy93YWl0aW5nIGZvciBpdHMgY29tcGxldGVMb2FkIHRvIGZpcmUuCiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChmb3VuZCkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgZm91bmQgPSB0cnVlOwogICAgICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAoYXJnc1swXSA9PT0gbW9kdWxlTmFtZSkgewogICAgICAgICAgICAgICAgICAgICAgICAvL0ZvdW5kIG1hdGNoaW5nIGRlZmluZSBjYWxsIGZvciB0aGlzIHNjcmlwdCEKICAgICAgICAgICAgICAgICAgICAgICAgZm91bmQgPSB0cnVlOwogICAgICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICAgICAgY2FsbEdldE1vZHVsZShhcmdzKTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIGNvbnRleHQuZGVmUXVldWVNYXAgPSB7fTsKCiAgICAgICAgICAgICAgICAvL0RvIHRoaXMgYWZ0ZXIgdGhlIGN5Y2xlIG9mIGNhbGxHZXRNb2R1bGUgaW4gY2FzZSB0aGUgcmVzdWx0CiAgICAgICAgICAgICAgICAvL29mIHRob3NlIGNhbGxzL2luaXQgY2FsbHMgY2hhbmdlcyB0aGUgcmVnaXN0cnkuCiAgICAgICAgICAgICAgICBtb2QgPSBnZXRPd24ocmVnaXN0cnksIG1vZHVsZU5hbWUpOwoKICAgICAgICAgICAgICAgIGlmICghZm91bmQgJiYgIWhhc1Byb3AoZGVmaW5lZCwgbW9kdWxlTmFtZSkgJiYgbW9kICYmICFtb2QuaW5pdGVkKSB7CiAgICAgICAgICAgICAgICAgICAgaWYgKGNvbmZpZy5lbmZvcmNlRGVmaW5lICYmICghc2hFeHBvcnRzIHx8ICFnZXRHbG9iYWwoc2hFeHBvcnRzKSkpIHsKICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGhhc1BhdGhGYWxsYmFjayhtb2R1bGVOYW1lKSkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuOwogICAgICAgICAgICAgICAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIG9uRXJyb3IobWFrZUVycm9yKCdub2RlZmluZScsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdObyBkZWZpbmUgY2FsbCBmb3IgJyArIG1vZHVsZU5hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG51bGwsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFttb2R1bGVOYW1lXSkpOwogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgICAgICAgICAgICAgLy9BIHNjcmlwdCB0aGF0IGRvZXMgbm90IGNhbGwgZGVmaW5lKCksIHNvIGp1c3Qgc2ltdWxhdGUKICAgICAgICAgICAgICAgICAgICAgICAgLy90aGUgY2FsbCBmb3IgaXQuCiAgICAgICAgICAgICAgICAgICAgICAgIGNhbGxHZXRNb2R1bGUoW21vZHVsZU5hbWUsIChzaGltLmRlcHMgfHwgW10pLCBzaGltLmV4cG9ydHNGbl0pOwogICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICBjaGVja0xvYWRlZCgpOwogICAgICAgICAgICB9LAoKICAgICAgICAgICAgLyoqCiAgICAgICAgICAgICAqIENvbnZlcnRzIGEgbW9kdWxlIG5hbWUgdG8gYSBmaWxlIHBhdGguIFN1cHBvcnRzIGNhc2VzIHdoZXJlCiAgICAgICAgICAgICAqIG1vZHVsZU5hbWUgbWF5IGFjdHVhbGx5IGJlIGp1c3QgYW4gVVJMLgogICAgICAgICAgICAgKiBOb3RlIHRoYXQgaXQgKipkb2VzIG5vdCoqIGNhbGwgbm9ybWFsaXplIG9uIHRoZSBtb2R1bGVOYW1lLAogICAgICAgICAgICAgKiBpdCBpcyBhc3N1bWVkIHRvIGhhdmUgYWxyZWFkeSBiZWVuIG5vcm1hbGl6ZWQuIFRoaXMgaXMgYW4KICAgICAgICAgICAgICogaW50ZXJuYWwgQVBJLCBub3QgYSBwdWJsaWMgb25lLiBVc2UgdG9VcmwgZm9yIHRoZSBwdWJsaWMgQVBJLgogICAgICAgICAgICAgKi8KICAgICAgICAgICAgbmFtZVRvVXJsOiBmdW5jdGlvbiAobW9kdWxlTmFtZSwgZXh0LCBza2lwRXh0KSB7CiAgICAgICAgICAgICAgICB2YXIgcGF0aHMsIHN5bXMsIGksIHBhcmVudE1vZHVsZSwgdXJsLAogICAgICAgICAgICAgICAgICAgIHBhcmVudFBhdGgsIGJ1bmRsZUlkLAogICAgICAgICAgICAgICAgICAgIHBrZ01haW4gPSBnZXRPd24oY29uZmlnLnBrZ3MsIG1vZHVsZU5hbWUpOwoKICAgICAgICAgICAgICAgIGlmIChwa2dNYWluKSB7CiAgICAgICAgICAgICAgICAgICAgbW9kdWxlTmFtZSA9IHBrZ01haW47CiAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgYnVuZGxlSWQgPSBnZXRPd24oYnVuZGxlc01hcCwgbW9kdWxlTmFtZSk7CgogICAgICAgICAgICAgICAgaWYgKGJ1bmRsZUlkKSB7CiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGNvbnRleHQubmFtZVRvVXJsKGJ1bmRsZUlkLCBleHQsIHNraXBFeHQpOwogICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgIC8vSWYgYSBjb2xvbiBpcyBpbiB0aGUgVVJMLCBpdCBpbmRpY2F0ZXMgYSBwcm90b2NvbCBpcyB1c2VkIGFuZCBpdCBpcyBqdXN0CiAgICAgICAgICAgICAgICAvL2FuIFVSTCB0byBhIGZpbGUsIG9yIGlmIGl0IHN0YXJ0cyB3aXRoIGEgc2xhc2gsIGNvbnRhaW5zIGEgcXVlcnkgYXJnIChpLmUuID8pCiAgICAgICAgICAgICAgICAvL29yIGVuZHMgd2l0aCAuanMsIHRoZW4gYXNzdW1lIHRoZSB1c2VyIG1lYW50IHRvIHVzZSBhbiB1cmwgYW5kIG5vdCBhIG1vZHVsZSBpZC4KICAgICAgICAgICAgICAgIC8vVGhlIHNsYXNoIGlzIGltcG9ydGFudCBmb3IgcHJvdG9jb2wtbGVzcyBVUkxzIGFzIHdlbGwgYXMgZnVsbCBwYXRocy4KICAgICAgICAgICAgICAgIGlmIChyZXEuanNFeHRSZWdFeHAudGVzdChtb2R1bGVOYW1lKSkgewogICAgICAgICAgICAgICAgICAgIC8vSnVzdCBhIHBsYWluIHBhdGgsIG5vdCBtb2R1bGUgbmFtZSBsb29rdXAsIHNvIGp1c3QgcmV0dXJuIGl0LgogICAgICAgICAgICAgICAgICAgIC8vQWRkIGV4dGVuc2lvbiBpZiBpdCBpcyBpbmNsdWRlZC4gVGhpcyBpcyBhIGJpdCB3b25reSwgb25seSBub24tLmpzIHRoaW5ncyBwYXNzCiAgICAgICAgICAgICAgICAgICAgLy9hbiBleHRlbnNpb24sIHRoaXMgbWV0aG9kIHByb2JhYmx5IG5lZWRzIHRvIGJlIHJld29ya2VkLgogICAgICAgICAgICAgICAgICAgIHVybCA9IG1vZHVsZU5hbWUgKyAoZXh0IHx8ICcnKTsKICAgICAgICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgICAgICAgICAgLy9BIG1vZHVsZSB0aGF0IG5lZWRzIHRvIGJlIGNvbnZlcnRlZCB0byBhIHBhdGguCiAgICAgICAgICAgICAgICAgICAgcGF0aHMgPSBjb25maWcucGF0aHM7CgogICAgICAgICAgICAgICAgICAgIHN5bXMgPSBtb2R1bGVOYW1lLnNwbGl0KCcvJyk7CiAgICAgICAgICAgICAgICAgICAgLy9Gb3IgZWFjaCBtb2R1bGUgbmFtZSBzZWdtZW50LCBzZWUgaWYgdGhlcmUgaXMgYSBwYXRoCiAgICAgICAgICAgICAgICAgICAgLy9yZWdpc3RlcmVkIGZvciBpdC4gU3RhcnQgd2l0aCBtb3N0IHNwZWNpZmljIG5hbWUKICAgICAgICAgICAgICAgICAgICAvL2FuZCB3b3JrIHVwIGZyb20gaXQuCiAgICAgICAgICAgICAgICAgICAgZm9yIChpID0gc3ltcy5sZW5ndGg7IGkgPiAwOyBpIC09IDEpIHsKICAgICAgICAgICAgICAgICAgICAgICAgcGFyZW50TW9kdWxlID0gc3ltcy5zbGljZSgwLCBpKS5qb2luKCcvJyk7CgogICAgICAgICAgICAgICAgICAgICAgICBwYXJlbnRQYXRoID0gZ2V0T3duKHBhdGhzLCBwYXJlbnRNb2R1bGUpOwogICAgICAgICAgICAgICAgICAgICAgICBpZiAocGFyZW50UGF0aCkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgLy9JZiBhbiBhcnJheSwgaXQgbWVhbnMgdGhlcmUgYXJlIGEgZmV3IGNob2ljZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvL0Nob29zZSB0aGUgb25lIHRoYXQgaXMgZGVzaXJlZAogICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGlzQXJyYXkocGFyZW50UGF0aCkpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXJlbnRQYXRoID0gcGFyZW50UGF0aFswXTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN5bXMuc3BsaWNlKDAsIGksIHBhcmVudFBhdGgpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgICAgIC8vSm9pbiB0aGUgcGF0aCBwYXJ0cyB0b2dldGhlciwgdGhlbiBmaWd1cmUgb3V0IGlmIGJhc2VVcmwgaXMgbmVlZGVkLgogICAgICAgICAgICAgICAgICAgIHVybCA9IHN5bXMuam9pbignLycpOwogICAgICAgICAgICAgICAgICAgIHVybCArPSAoZXh0IHx8ICgvXmRhdGFcOnxcPy8udGVzdCh1cmwpIHx8IHNraXBFeHQgPyAnJyA6ICcuanMnKSk7CiAgICAgICAgICAgICAgICAgICAgdXJsID0gKHVybC5jaGFyQXQoMCkgPT09ICcvJyB8fCB1cmwubWF0Y2goL15bXHdcK1wuXC1dKzovKSA/ICcnIDogY29uZmlnLmJhc2VVcmwpICsgdXJsOwogICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgIHJldHVybiBjb25maWcudXJsQXJncyA/IHVybCArCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoKHVybC5pbmRleE9mKCc/JykgPT09IC0xID8gJz8nIDogJyYnKSArCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29uZmlnLnVybEFyZ3MpIDogdXJsOwogICAgICAgICAgICB9LAoKICAgICAgICAgICAgLy9EZWxlZ2F0ZXMgdG8gcmVxLmxvYWQuIEJyb2tlbiBvdXQgYXMgYSBzZXBhcmF0ZSBmdW5jdGlvbiB0bwogICAgICAgICAgICAvL2FsbG93IG92ZXJyaWRpbmcgaW4gdGhlIG9wdGltaXplci4KICAgICAgICAgICAgbG9hZDogZnVuY3Rpb24gKGlkLCB1cmwpIHsKICAgICAgICAgICAgICAgIHJlcS5sb2FkKGNvbnRleHQsIGlkLCB1cmwpOwogICAgICAgICAgICB9LAoKICAgICAgICAgICAgLyoqCiAgICAgICAgICAgICAqIEV4ZWN1dGVzIGEgbW9kdWxlIGNhbGxiYWNrIGZ1bmN0aW9uLiBCcm9rZW4gb3V0IGFzIGEgc2VwYXJhdGUgZnVuY3Rpb24KICAgICAgICAgICAgICogc29sZWx5IHRvIGFsbG93IHRoZSBidWlsZCBzeXN0ZW0gdG8gc2VxdWVuY2UgdGhlIGZpbGVzIGluIHRoZSBidWlsdAogICAgICAgICAgICAgKiBsYXllciBpbiB0aGUgcmlnaHQgc2VxdWVuY2UuCiAgICAgICAgICAgICAqCiAgICAgICAgICAgICAqIEBwcml2YXRlCiAgICAgICAgICAgICAqLwogICAgICAgICAgICBleGVjQ2I6IGZ1bmN0aW9uIChuYW1lLCBjYWxsYmFjaywgYXJncywgZXhwb3J0cykgewogICAgICAgICAgICAgICAgcmV0dXJuIGNhbGxiYWNrLmFwcGx5KGV4cG9ydHMsIGFyZ3MpOwogICAgICAgICAgICB9LAoKICAgICAgICAgICAgLyoqCiAgICAgICAgICAgICAqIGNhbGxiYWNrIGZvciBzY3JpcHQgbG9hZHMsIHVzZWQgdG8gY2hlY2sgc3RhdHVzIG9mIGxvYWRpbmcuCiAgICAgICAgICAgICAqCiAgICAgICAgICAgICAqIEBwYXJhbSB7RXZlbnR9IGV2dCB0aGUgZXZlbnQgZnJvbSB0aGUgYnJvd3NlciBmb3IgdGhlIHNjcmlwdAogICAgICAgICAgICAgKiB0aGF0IHdhcyBsb2FkZWQuCiAgICAgICAgICAgICAqLwogICAgICAgICAgICBvblNjcmlwdExvYWQ6IGZ1bmN0aW9uIChldnQpIHsKICAgICAgICAgICAgICAgIC8vVXNpbmcgY3VycmVudFRhcmdldCBpbnN0ZWFkIG9mIHRhcmdldCBmb3IgRmlyZWZveCAyLjAncyBzYWtlLiBOb3QKICAgICAgICAgICAgICAgIC8vYWxsIG9sZCBicm93c2VycyB3aWxsIGJlIHN1cHBvcnRlZCwgYnV0IHRoaXMgb25lIHdhcyBlYXN5IGVub3VnaAogICAgICAgICAgICAgICAgLy90byBzdXBwb3J0IGFuZCBzdGlsbCBtYWtlcyBzZW5zZS4KICAgICAgICAgICAgICAgIGlmIChldnQudHlwZSA9PT0gJ2xvYWQnIHx8CiAgICAgICAgICAgICAgICAgICAgICAgIChyZWFkeVJlZ0V4cC50ZXN0KChldnQuY3VycmVudFRhcmdldCB8fCBldnQuc3JjRWxlbWVudCkucmVhZHlTdGF0ZSkpKSB7CiAgICAgICAgICAgICAgICAgICAgLy9SZXNldCBpbnRlcmFjdGl2ZSBzY3JpcHQgc28gYSBzY3JpcHQgbm9kZSBpcyBub3QgaGVsZCBvbnRvIGZvcgogICAgICAgICAgICAgICAgICAgIC8vdG8gbG9uZy4KICAgICAgICAgICAgICAgICAgICBpbnRlcmFjdGl2ZVNjcmlwdCA9IG51bGw7CgogICAgICAgICAgICAgICAgICAgIC8vUHVsbCBvdXQgdGhlIG5hbWUgb2YgdGhlIG1vZHVsZSBhbmQgdGhlIGNvbnRleHQuCiAgICAgICAgICAgICAgICAgICAgdmFyIGRhdGEgPSBnZXRTY3JpcHREYXRhKGV2dCk7CiAgICAgICAgICAgICAgICAgICAgY29udGV4dC5jb21wbGV0ZUxvYWQoZGF0YS5pZCk7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCgogICAgICAgICAgICAvKioKICAgICAgICAgICAgICogQ2FsbGJhY2sgZm9yIHNjcmlwdCBlcnJvcnMuCiAgICAgICAgICAgICAqLwogICAgICAgICAgICBvblNjcmlwdEVycm9yOiBmdW5jdGlvbiAoZXZ0KSB7CiAgICAgICAgICAgICAgICB2YXIgZGF0YSA9IGdldFNjcmlwdERhdGEoZXZ0KTsKICAgICAgICAgICAgICAgIGlmICghaGFzUGF0aEZhbGxiYWNrKGRhdGEuaWQpKSB7CiAgICAgICAgICAgICAgICAgICAgdmFyIHBhcmVudHMgPSBbXTsKICAgICAgICAgICAgICAgICAgICBlYWNoUHJvcChyZWdpc3RyeSwgZnVuY3Rpb24odmFsdWUsIGtleSkgewogICAgICAgICAgICAgICAgICAgICAgICBpZiAoa2V5LmluZGV4T2YoJ19AcicpICE9PSAwKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBlYWNoKHZhbHVlLmRlcE1hcHMsIGZ1bmN0aW9uKGRlcE1hcCkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIChkZXBNYXAuaWQgPT09IGRhdGEuaWQpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFyZW50cy5wdXNoKGtleSk7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiB0cnVlOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgfSk7CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICB9KTsKICAgICAgICAgICAgICAgICAgICByZXR1cm4gb25FcnJvcihtYWtlRXJyb3IoJ3NjcmlwdGVycm9yJywgJ1NjcmlwdCBlcnJvciBmb3IgIicgKyBkYXRhLmlkICsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKHBhcmVudHMubGVuZ3RoID8KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJyIsIG5lZWRlZCBieTogJyArIHBhcmVudHMuam9pbignLCAnKSA6CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICciJyksIGV2dCwgW2RhdGEuaWRdKSk7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICB9OwoKICAgICAgICBjb250ZXh0LnJlcXVpcmUgPSBjb250ZXh0Lm1ha2VSZXF1aXJlKCk7CiAgICAgICAgcmV0dXJuIGNvbnRleHQ7CiAgICB9CgogICAgLyoqCiAgICAgKiBNYWluIGVudHJ5IHBvaW50LgogICAgICoKICAgICAqIElmIHRoZSBvbmx5IGFyZ3VtZW50IHRvIHJlcXVpcmUgaXMgYSBzdHJpbmcsIHRoZW4gdGhlIG1vZHVsZSB0aGF0CiAgICAgKiBpcyByZXByZXNlbnRlZCBieSB0aGF0IHN0cmluZyBpcyBmZXRjaGVkIGZvciB0aGUgYXBwcm9wcmlhdGUgY29udGV4dC4KICAgICAqCiAgICAgKiBJZiB0aGUgZmlyc3QgYXJndW1lbnQgaXMgYW4gYXJyYXksIHRoZW4gaXQgd2lsbCBiZSB0cmVhdGVkIGFzIGFuIGFycmF5CiAgICAgKiBvZiBkZXBlbmRlbmN5IHN0cmluZyBuYW1lcyB0byBmZXRjaC4gQW4gb3B0aW9uYWwgZnVuY3Rpb24gY2FsbGJhY2sgY2FuCiAgICAgKiBiZSBzcGVjaWZpZWQgdG8gZXhlY3V0ZSB3aGVuIGFsbCBvZiB0aG9zZSBkZXBlbmRlbmNpZXMgYXJlIGF2YWlsYWJsZS4KICAgICAqCiAgICAgKiBNYWtlIGEgbG9jYWwgcmVxIHZhcmlhYmxlIHRvIGhlbHAgQ2FqYSBjb21wbGlhbmNlIChpdCBhc3N1bWVzIHRoaW5ncwogICAgICogb24gYSByZXF1aXJlIHRoYXQgYXJlIG5vdCBzdGFuZGFyZGl6ZWQpLCBhbmQgdG8gZ2l2ZSBhIHNob3J0CiAgICAgKiBuYW1lIGZvciBtaW5pZmljYXRpb24vbG9jYWwgc2NvcGUgdXNlLgogICAgICovCiAgICByZXEgPSByZXF1aXJlanMgPSBmdW5jdGlvbiAoZGVwcywgY2FsbGJhY2ssIGVycmJhY2ssIG9wdGlvbmFsKSB7CgogICAgICAgIC8vRmluZCB0aGUgcmlnaHQgY29udGV4dCwgdXNlIGRlZmF1bHQKICAgICAgICB2YXIgY29udGV4dCwgY29uZmlnLAogICAgICAgICAgICBjb250ZXh0TmFtZSA9IGRlZkNvbnRleHROYW1lOwoKICAgICAgICAvLyBEZXRlcm1pbmUgaWYgaGF2ZSBjb25maWcgb2JqZWN0IGluIHRoZSBjYWxsLgogICAgICAgIGlmICghaXNBcnJheShkZXBzKSAmJiB0eXBlb2YgZGVwcyAhPT0gJ3N0cmluZycpIHsKICAgICAgICAgICAgLy8gZGVwcyBpcyBhIGNvbmZpZyBvYmplY3QKICAgICAgICAgICAgY29uZmlnID0gZGVwczsKICAgICAgICAgICAgaWYgKGlzQXJyYXkoY2FsbGJhY2spKSB7CiAgICAgICAgICAgICAgICAvLyBBZGp1c3QgYXJncyBpZiB0aGVyZSBhcmUgZGVwZW5kZW5jaWVzCiAgICAgICAgICAgICAgICBkZXBzID0gY2FsbGJhY2s7CiAgICAgICAgICAgICAgICBjYWxsYmFjayA9IGVycmJhY2s7CiAgICAgICAgICAgICAgICBlcnJiYWNrID0gb3B0aW9uYWw7CiAgICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgICAgICBkZXBzID0gW107CiAgICAgICAgICAgIH0KICAgICAgICB9CgogICAgICAgIGlmIChjb25maWcgJiYgY29uZmlnLmNvbnRleHQpIHsKICAgICAgICAgICAgY29udGV4dE5hbWUgPSBjb25maWcuY29udGV4dDsKICAgICAgICB9CgogICAgICAgIGNvbnRleHQgPSBnZXRPd24oY29udGV4dHMsIGNvbnRleHROYW1lKTsKICAgICAgICBpZiAoIWNvbnRleHQpIHsKICAgICAgICAgICAgY29udGV4dCA9IGNvbnRleHRzW2NvbnRleHROYW1lXSA9IHJlcS5zLm5ld0NvbnRleHQoY29udGV4dE5hbWUpOwogICAgICAgIH0KCiAgICAgICAgaWYgKGNvbmZpZykgewogICAgICAgICAgICBjb250ZXh0LmNvbmZpZ3VyZShjb25maWcpOwogICAgICAgIH0KCiAgICAgICAgcmV0dXJuIGNvbnRleHQucmVxdWlyZShkZXBzLCBjYWxsYmFjaywgZXJyYmFjayk7CiAgICB9OwoKICAgIC8qKgogICAgICogU3VwcG9ydCByZXF1aXJlLmNvbmZpZygpIHRvIG1ha2UgaXQgZWFzaWVyIHRvIGNvb3BlcmF0ZSB3aXRoIG90aGVyCiAgICAgKiBBTUQgbG9hZGVycyBvbiBnbG9iYWxseSBhZ3JlZWQgbmFtZXMuCiAgICAgKi8KICAgIHJlcS5jb25maWcgPSBmdW5jdGlvbiAoY29uZmlnKSB7CiAgICAgICAgcmV0dXJuIHJlcShjb25maWcpOwogICAgfTsKCiAgICAvKioKICAgICAqIEV4ZWN1dGUgc29tZXRoaW5nIGFmdGVyIHRoZSBjdXJyZW50IHRpY2sKICAgICAqIG9mIHRoZSBldmVudCBsb29wLiBPdmVycmlkZSBmb3Igb3RoZXIgZW52cwogICAgICogdGhhdCBoYXZlIGEgYmV0dGVyIHNvbHV0aW9uIHRoYW4gc2V0VGltZW91dC4KICAgICAqIEBwYXJhbSAge0Z1bmN0aW9ufSBmbiBmdW5jdGlvbiB0byBleGVjdXRlIGxhdGVyLgogICAgICovCiAgICByZXEubmV4dFRpY2sgPSB0eXBlb2Ygc2V0VGltZW91dCAhPT0gJ3VuZGVmaW5lZCcgPyBmdW5jdGlvbiAoZm4pIHsKICAgICAgICBzZXRUaW1lb3V0KGZuLCA0KTsKICAgIH0gOiBmdW5jdGlvbiAoZm4pIHsgZm4oKTsgfTsKCiAgICAvKioKICAgICAqIEV4cG9ydCByZXF1aXJlIGFzIGEgZ2xvYmFsLCBidXQgb25seSBpZiBpdCBkb2VzIG5vdCBhbHJlYWR5IGV4aXN0LgogICAgICovCiAgICBpZiAoIXJlcXVpcmUpIHsKICAgICAgICByZXF1aXJlID0gcmVxOwogICAgfQoKICAgIHJlcS52ZXJzaW9uID0gdmVyc2lvbjsKCiAgICAvL1VzZWQgdG8gZmlsdGVyIG91dCBkZXBlbmRlbmNpZXMgdGhhdCBhcmUgYWxyZWFkeSBwYXRocy4KICAgIHJlcS5qc0V4dFJlZ0V4cCA9IC9eXC98OnxcP3xcLmpzJC87CiAgICByZXEuaXNCcm93c2VyID0gaXNCcm93c2VyOwogICAgcyA9IHJlcS5zID0gewogICAgICAgIGNvbnRleHRzOiBjb250ZXh0cywKICAgICAgICBuZXdDb250ZXh0OiBuZXdDb250ZXh0CiAgICB9OwoKICAgIC8vQ3JlYXRlIGRlZmF1bHQgY29udGV4dC4KICAgIHJlcSh7fSk7CgogICAgLy9FeHBvcnRzIHNvbWUgY29udGV4dC1zZW5zaXRpdmUgbWV0aG9kcyBvbiBnbG9iYWwgcmVxdWlyZS4KICAgIGVhY2goWwogICAgICAgICd0b1VybCcsCiAgICAgICAgJ3VuZGVmJywKICAgICAgICAnZGVmaW5lZCcsCiAgICAgICAgJ3NwZWNpZmllZCcKICAgIF0sIGZ1bmN0aW9uIChwcm9wKSB7CiAgICAgICAgLy9SZWZlcmVuY2UgZnJvbSBjb250ZXh0cyBpbnN0ZWFkIG9mIGVhcmx5IGJpbmRpbmcgdG8gZGVmYXVsdCBjb250ZXh0LAogICAgICAgIC8vc28gdGhhdCBkdXJpbmcgYnVpbGRzLCB0aGUgbGF0ZXN0IGluc3RhbmNlIG9mIHRoZSBkZWZhdWx0IGNvbnRleHQKICAgICAgICAvL3dpdGggaXRzIGNvbmZpZyBnZXRzIHVzZWQuCiAgICAgICAgcmVxW3Byb3BdID0gZnVuY3Rpb24gKCkgewogICAgICAgICAgICB2YXIgY3R4ID0gY29udGV4dHNbZGVmQ29udGV4dE5hbWVdOwogICAgICAgICAgICByZXR1cm4gY3R4LnJlcXVpcmVbcHJvcF0uYXBwbHkoY3R4LCBhcmd1bWVudHMpOwogICAgICAgIH07CiAgICB9KTsKCiAgICBpZiAoaXNCcm93c2VyKSB7CiAgICAgICAgaGVhZCA9IHMuaGVhZCA9IGRvY3VtZW50LmdldEVsZW1lbnRzQnlUYWdOYW1lKCdoZWFkJylbMF07CiAgICAgICAgLy9JZiBCQVNFIHRhZyBpcyBpbiBwbGF5LCB1c2luZyBhcHBlbmRDaGlsZCBpcyBhIHByb2JsZW0gZm9yIElFNi4KICAgICAgICAvL1doZW4gdGhhdCBicm93c2VyIGRpZXMsIHRoaXMgY2FuIGJlIHJlbW92ZWQuIERldGFpbHMgaW4gdGhpcyBqUXVlcnkgYnVnOgogICAgICAgIC8vaHR0cDovL2Rldi5qcXVlcnkuY29tL3RpY2tldC8yNzA5CiAgICAgICAgYmFzZUVsZW1lbnQgPSBkb2N1bWVudC5nZXRFbGVtZW50c0J5VGFnTmFtZSgnYmFzZScpWzBdOwogICAgICAgIGlmIChiYXNlRWxlbWVudCkgewogICAgICAgICAgICBoZWFkID0gcy5oZWFkID0gYmFzZUVsZW1lbnQucGFyZW50Tm9kZTsKICAgICAgICB9CiAgICB9CgogICAgLyoqCiAgICAgKiBBbnkgZXJyb3JzIHRoYXQgcmVxdWlyZSBleHBsaWNpdGx5IGdlbmVyYXRlcyB3aWxsIGJlIHBhc3NlZCB0byB0aGlzCiAgICAgKiBmdW5jdGlvbi4gSW50ZXJjZXB0L292ZXJyaWRlIGl0IGlmIHlvdSB3YW50IGN1c3RvbSBlcnJvciBoYW5kbGluZy4KICAgICAqIEBwYXJhbSB7RXJyb3J9IGVyciB0aGUgZXJyb3Igb2JqZWN0LgogICAgICovCiAgICByZXEub25FcnJvciA9IGRlZmF1bHRPbkVycm9yOwoKICAgIC8qKgogICAgICogQ3JlYXRlcyB0aGUgbm9kZSBmb3IgdGhlIGxvYWQgY29tbWFuZC4gT25seSB1c2VkIGluIGJyb3dzZXIgZW52cy4KICAgICAqLwogICAgcmVxLmNyZWF0ZU5vZGUgPSBmdW5jdGlvbiAoY29uZmlnLCBtb2R1bGVOYW1lLCB1cmwpIHsKICAgICAgICB2YXIgbm9kZSA9IGNvbmZpZy54aHRtbCA/CiAgICAgICAgICAgICAgICBkb2N1bWVudC5jcmVhdGVFbGVtZW50TlMoJ2h0dHA6Ly93d3cudzMub3JnLzE5OTkveGh0bWwnLCAnaHRtbDpzY3JpcHQnKSA6CiAgICAgICAgICAgICAgICBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzY3JpcHQnKTsKICAgICAgICBub2RlLnR5cGUgPSBjb25maWcuc2NyaXB0VHlwZSB8fCAndGV4dC9qYXZhc2NyaXB0JzsKICAgICAgICBub2RlLmNoYXJzZXQgPSAndXRmLTgnOwogICAgICAgIG5vZGUuYXN5bmMgPSB0cnVlOwogICAgICAgIHJldHVybiBub2RlOwogICAgfTsKCiAgICAvKioKICAgICAqIERvZXMgdGhlIHJlcXVlc3QgdG8gbG9hZCBhIG1vZHVsZSBmb3IgdGhlIGJyb3dzZXIgY2FzZS4KICAgICAqIE1ha2UgdGhpcyBhIHNlcGFyYXRlIGZ1bmN0aW9uIHRvIGFsbG93IG90aGVyIGVudmlyb25tZW50cwogICAgICogdG8gb3ZlcnJpZGUgaXQuCiAgICAgKgogICAgICogQHBhcmFtIHtPYmplY3R9IGNvbnRleHQgdGhlIHJlcXVpcmUgY29udGV4dCB0byBmaW5kIHN0YXRlLgogICAgICogQHBhcmFtIHtTdHJpbmd9IG1vZHVsZU5hbWUgdGhlIG5hbWUgb2YgdGhlIG1vZHVsZS4KICAgICAqIEBwYXJhbSB7T2JqZWN0fSB1cmwgdGhlIFVSTCB0byB0aGUgbW9kdWxlLgogICAgICovCiAgICByZXEubG9hZCA9IGZ1bmN0aW9uIChjb250ZXh0LCBtb2R1bGVOYW1lLCB1cmwpIHsKICAgICAgICB2YXIgY29uZmlnID0gKGNvbnRleHQgJiYgY29udGV4dC5jb25maWcpIHx8IHt9LAogICAgICAgICAgICBub2RlOwogICAgICAgIGlmIChpc0Jyb3dzZXIpIHsKICAgICAgICAgICAgLy9JbiB0aGUgYnJvd3NlciBzbyB1c2UgYSBzY3JpcHQgdGFnCiAgICAgICAgICAgIG5vZGUgPSByZXEuY3JlYXRlTm9kZShjb25maWcsIG1vZHVsZU5hbWUsIHVybCk7CiAgICAgICAgICAgIGlmIChjb25maWcub25Ob2RlQ3JlYXRlZCkgewogICAgICAgICAgICAgICAgY29uZmlnLm9uTm9kZUNyZWF0ZWQobm9kZSwgY29uZmlnLCBtb2R1bGVOYW1lLCB1cmwpOwogICAgICAgICAgICB9CgogICAgICAgICAgICBub2RlLnNldEF0dHJpYnV0ZSgnZGF0YS1yZXF1aXJlY29udGV4dCcsIGNvbnRleHQuY29udGV4dE5hbWUpOwogICAgICAgICAgICBub2RlLnNldEF0dHJpYnV0ZSgnZGF0YS1yZXF1aXJlbW9kdWxlJywgbW9kdWxlTmFtZSk7CgogICAgICAgICAgICAvL1NldCB1cCBsb2FkIGxpc3RlbmVyLiBUZXN0IGF0dGFjaEV2ZW50IGZpcnN0IGJlY2F1c2UgSUU5IGhhcwogICAgICAgICAgICAvL2Egc3VidGxlIGlzc3VlIGluIGl0cyBhZGRFdmVudExpc3RlbmVyIGFuZCBzY3JpcHQgb25sb2FkIGZpcmluZ3MKICAgICAgICAgICAgLy90aGF0IGRvIG5vdCBtYXRjaCB0aGUgYmVoYXZpb3Igb2YgYWxsIG90aGVyIGJyb3dzZXJzIHdpdGgKICAgICAgICAgICAgLy9hZGRFdmVudExpc3RlbmVyIHN1cHBvcnQsIHdoaWNoIGZpcmUgdGhlIG9ubG9hZCBldmVudCBmb3IgYQogICAgICAgICAgICAvL3NjcmlwdCByaWdodCBhZnRlciB0aGUgc2NyaXB0IGV4ZWN1dGlvbi4gU2VlOgogICAgICAgICAgICAvL2h0dHBzOi8vY29ubmVjdC5taWNyb3NvZnQuY29tL0lFL2ZlZWRiYWNrL2RldGFpbHMvNjQ4MDU3L3NjcmlwdC1vbmxvYWQtZXZlbnQtaXMtbm90LWZpcmVkLWltbWVkaWF0ZWx5LWFmdGVyLXNjcmlwdC1leGVjdXRpb24KICAgICAgICAgICAgLy9VTkZPUlRVTkFURUxZIE9wZXJhIGltcGxlbWVudHMgYXR0YWNoRXZlbnQgYnV0IGRvZXMgbm90IGZvbGxvdyB0aGUgc2NyaXB0CiAgICAgICAgICAgIC8vc2NyaXB0IGV4ZWN1dGlvbiBtb2RlLgogICAgICAgICAgICBpZiAobm9kZS5hdHRhY2hFdmVudCAmJgogICAgICAgICAgICAgICAgICAgIC8vQ2hlY2sgaWYgbm9kZS5hdHRhY2hFdmVudCBpcyBhcnRpZmljaWFsbHkgYWRkZWQgYnkgY3VzdG9tIHNjcmlwdCBvcgogICAgICAgICAgICAgICAgICAgIC8vbmF0aXZlbHkgc3VwcG9ydGVkIGJ5IGJyb3dzZXIKICAgICAgICAgICAgICAgICAgICAvL3JlYWQgaHR0cHM6Ly9naXRodWIuY29tL2pyYnVya2UvcmVxdWlyZWpzL2lzc3Vlcy8xODcKICAgICAgICAgICAgICAgICAgICAvL2lmIHdlIGNhbiBOT1QgZmluZCBbbmF0aXZlIGNvZGVdIHRoZW4gaXQgbXVzdCBOT1QgbmF0aXZlbHkgc3VwcG9ydGVkLgogICAgICAgICAgICAgICAgICAgIC8vaW4gSUU4LCBub2RlLmF0dGFjaEV2ZW50IGRvZXMgbm90IGhhdmUgdG9TdHJpbmcoKQogICAgICAgICAgICAgICAgICAgIC8vTm90ZSB0aGUgdGVzdCBmb3IgIltuYXRpdmUgY29kZSIgd2l0aCBubyBjbG9zaW5nIGJyYWNlLCBzZWU6CiAgICAgICAgICAgICAgICAgICAgLy9odHRwczovL2dpdGh1Yi5jb20vanJidXJrZS9yZXF1aXJlanMvaXNzdWVzLzI3MwogICAgICAgICAgICAgICAgICAgICEobm9kZS5hdHRhY2hFdmVudC50b1N0cmluZyAmJiBub2RlLmF0dGFjaEV2ZW50LnRvU3RyaW5nKCkuaW5kZXhPZignW25hdGl2ZSBjb2RlJykgPCAwKSAmJgogICAgICAgICAgICAgICAgICAgICFpc09wZXJhKSB7CiAgICAgICAgICAgICAgICAvL1Byb2JhYmx5IElFLiBJRSAoYXQgbGVhc3QgNi04KSBkbyBub3QgZmlyZQogICAgICAgICAgICAgICAgLy9zY3JpcHQgb25sb2FkIHJpZ2h0IGFmdGVyIGV4ZWN1dGluZyB0aGUgc2NyaXB0LCBzbwogICAgICAgICAgICAgICAgLy93ZSBjYW5ub3QgdGllIHRoZSBhbm9ueW1vdXMgZGVmaW5lIGNhbGwgdG8gYSBuYW1lLgogICAgICAgICAgICAgICAgLy9Ib3dldmVyLCBJRSByZXBvcnRzIHRoZSBzY3JpcHQgYXMgYmVpbmcgaW4gJ2ludGVyYWN0aXZlJwogICAgICAgICAgICAgICAgLy9yZWFkeVN0YXRlIGF0IHRoZSB0aW1lIG9mIHRoZSBkZWZpbmUgY2FsbC4KICAgICAgICAgICAgICAgIHVzZUludGVyYWN0aXZlID0gdHJ1ZTsKCiAgICAgICAgICAgICAgICBub2RlLmF0dGFjaEV2ZW50KCdvbnJlYWR5c3RhdGVjaGFuZ2UnLCBjb250ZXh0Lm9uU2NyaXB0TG9hZCk7CiAgICAgICAgICAgICAgICAvL0l0IHdvdWxkIGJlIGdyZWF0IHRvIGFkZCBhbiBlcnJvciBoYW5kbGVyIGhlcmUgdG8gY2F0Y2gKICAgICAgICAgICAgICAgIC8vNDA0cyBpbiBJRTkrLiBIb3dldmVyLCBvbnJlYWR5c3RhdGVjaGFuZ2Ugd2lsbCBmaXJlIGJlZm9yZQogICAgICAgICAgICAgICAgLy90aGUgZXJyb3IgaGFuZGxlciwgc28gdGhhdCBkb2VzIG5vdCBoZWxwLiBJZiBhZGRFdmVudExpc3RlbmVyCiAgICAgICAgICAgICAgICAvL2lzIHVzZWQsIHRoZW4gSUUgd2lsbCBmaXJlIGVycm9yIGJlZm9yZSBsb2FkLCBidXQgd2UgY2Fubm90CiAgICAgICAgICAgICAgICAvL3VzZSB0aGF0IHBhdGh3YXkgZ2l2ZW4gdGhlIGNvbm5lY3QubWljcm9zb2Z0LmNvbSBpc3N1ZQogICAgICAgICAgICAgICAgLy9tZW50aW9uZWQgYWJvdmUgYWJvdXQgbm90IGRvaW5nIHRoZSAnc2NyaXB0IGV4ZWN1dGUsCiAgICAgICAgICAgICAgICAvL3RoZW4gZmlyZSB0aGUgc2NyaXB0IGxvYWQgZXZlbnQgbGlzdGVuZXIgYmVmb3JlIGV4ZWN1dGUKICAgICAgICAgICAgICAgIC8vbmV4dCBzY3JpcHQnIHRoYXQgb3RoZXIgYnJvd3NlcnMgZG8uCiAgICAgICAgICAgICAgICAvL0Jlc3QgaG9wZTogSUUxMCBmaXhlcyB0aGUgaXNzdWVzLAogICAgICAgICAgICAgICAgLy9hbmQgdGhlbiBkZXN0cm95cyBhbGwgaW5zdGFsbHMgb2YgSUUgNi05LgogICAgICAgICAgICAgICAgLy9ub2RlLmF0dGFjaEV2ZW50KCdvbmVycm9yJywgY29udGV4dC5vblNjcmlwdEVycm9yKTsKICAgICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgICAgIG5vZGUuYWRkRXZlbnRMaXN0ZW5lcignbG9hZCcsIGNvbnRleHQub25TY3JpcHRMb2FkLCBmYWxzZSk7CiAgICAgICAgICAgICAgICBub2RlLmFkZEV2ZW50TGlzdGVuZXIoJ2Vycm9yJywgY29udGV4dC5vblNjcmlwdEVycm9yLCBmYWxzZSk7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgbm9kZS5zcmMgPSB1cmw7CgogICAgICAgICAgICAvL0ZvciBzb21lIGNhY2hlIGNhc2VzIGluIElFIDYtOCwgdGhlIHNjcmlwdCBleGVjdXRlcyBiZWZvcmUgdGhlIGVuZAogICAgICAgICAgICAvL29mIHRoZSBhcHBlbmRDaGlsZCBleGVjdXRpb24sIHNvIHRvIHRpZSBhbiBhbm9ueW1vdXMgZGVmaW5lCiAgICAgICAgICAgIC8vY2FsbCB0byB0aGUgbW9kdWxlIG5hbWUgKHdoaWNoIGlzIHN0b3JlZCBvbiB0aGUgbm9kZSksIGhvbGQgb24KICAgICAgICAgICAgLy90byBhIHJlZmVyZW5jZSB0byB0aGlzIG5vZGUsIGJ1dCBjbGVhciBhZnRlciB0aGUgRE9NIGluc2VydGlvbi4KICAgICAgICAgICAgY3VycmVudGx5QWRkaW5nU2NyaXB0ID0gbm9kZTsKICAgICAgICAgICAgaWYgKGJhc2VFbGVtZW50KSB7CiAgICAgICAgICAgICAgICBoZWFkLmluc2VydEJlZm9yZShub2RlLCBiYXNlRWxlbWVudCk7CiAgICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgICAgICBoZWFkLmFwcGVuZENoaWxkKG5vZGUpOwogICAgICAgICAgICB9CiAgICAgICAgICAgIGN1cnJlbnRseUFkZGluZ1NjcmlwdCA9IG51bGw7CgogICAgICAgICAgICByZXR1cm4gbm9kZTsKICAgICAgICB9IGVsc2UgaWYgKGlzV2ViV29ya2VyKSB7CiAgICAgICAgICAgIHRyeSB7CiAgICAgICAgICAgICAgICAvL0luIGEgd2ViIHdvcmtlciwgdXNlIGltcG9ydFNjcmlwdHMuIFRoaXMgaXMgbm90IGEgdmVyeQogICAgICAgICAgICAgICAgLy9lZmZpY2llbnQgdXNlIG9mIGltcG9ydFNjcmlwdHMsIGltcG9ydFNjcmlwdHMgd2lsbCBibG9jayB1bnRpbAogICAgICAgICAgICAgICAgLy9pdHMgc2NyaXB0IGlzIGRvd25sb2FkZWQgYW5kIGV2YWx1YXRlZC4gSG93ZXZlciwgaWYgd2ViIHdvcmtlcnMKICAgICAgICAgICAgICAgIC8vYXJlIGluIHBsYXksIHRoZSBleHBlY3RhdGlvbiBpcyB0aGF0IGEgYnVpbGQgaGFzIGJlZW4gZG9uZSBzbwogICAgICAgICAgICAgICAgLy90aGF0IG9ubHkgb25lIHNjcmlwdCBuZWVkcyB0byBiZSBsb2FkZWQgYW55d2F5LiBUaGlzIG1heSBuZWVkCiAgICAgICAgICAgICAgICAvL3RvIGJlIHJlZXZhbHVhdGVkIGlmIG90aGVyIHVzZSBjYXNlcyBiZWNvbWUgY29tbW9uLgogICAgICAgICAgICAgICAgaW1wb3J0U2NyaXB0cyh1cmwpOwoKICAgICAgICAgICAgICAgIC8vQWNjb3VudCBmb3IgYW5vbnltb3VzIG1vZHVsZXMKICAgICAgICAgICAgICAgIGNvbnRleHQuY29tcGxldGVMb2FkKG1vZHVsZU5hbWUpOwogICAgICAgICAgICB9IGNhdGNoIChlKSB7CiAgICAgICAgICAgICAgICBjb250ZXh0Lm9uRXJyb3IobWFrZUVycm9yKCdpbXBvcnRzY3JpcHRzJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnaW1wb3J0U2NyaXB0cyBmYWlsZWQgZm9yICcgKwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtb2R1bGVOYW1lICsgJyBhdCAnICsgdXJsLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgW21vZHVsZU5hbWVdKSk7CiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICB9OwoKICAgIGZ1bmN0aW9uIGdldEludGVyYWN0aXZlU2NyaXB0KCkgewogICAgICAgIGlmIChpbnRlcmFjdGl2ZVNjcmlwdCAmJiBpbnRlcmFjdGl2ZVNjcmlwdC5yZWFkeVN0YXRlID09PSAnaW50ZXJhY3RpdmUnKSB7CiAgICAgICAgICAgIHJldHVybiBpbnRlcmFjdGl2ZVNjcmlwdDsKICAgICAgICB9CgogICAgICAgIGVhY2hSZXZlcnNlKHNjcmlwdHMoKSwgZnVuY3Rpb24gKHNjcmlwdCkgewogICAgICAgICAgICBpZiAoc2NyaXB0LnJlYWR5U3RhdGUgPT09ICdpbnRlcmFjdGl2ZScpIHsKICAgICAgICAgICAgICAgIHJldHVybiAoaW50ZXJhY3RpdmVTY3JpcHQgPSBzY3JpcHQpOwogICAgICAgICAgICB9CiAgICAgICAgfSk7CiAgICAgICAgcmV0dXJuIGludGVyYWN0aXZlU2NyaXB0OwogICAgfQoKICAgIC8vTG9vayBmb3IgYSBkYXRhLW1haW4gc2NyaXB0IGF0dHJpYnV0ZSwgd2hpY2ggY291bGQgYWxzbyBhZGp1c3QgdGhlIGJhc2VVcmwuCiAgICBpZiAoaXNCcm93c2VyICYmICFjZmcuc2tpcERhdGFNYWluKSB7CiAgICAgICAgLy9GaWd1cmUgb3V0IGJhc2VVcmwuIEdldCBpdCBmcm9tIHRoZSBzY3JpcHQgdGFnIHdpdGggcmVxdWlyZS5qcyBpbiBpdC4KICAgICAgICBlYWNoUmV2ZXJzZShzY3JpcHRzKCksIGZ1bmN0aW9uIChzY3JpcHQpIHsKICAgICAgICAgICAgLy9TZXQgdGhlICdoZWFkJyB3aGVyZSB3ZSBjYW4gYXBwZW5kIGNoaWxkcmVuIGJ5CiAgICAgICAgICAgIC8vdXNpbmcgdGhlIHNjcmlwdCdzIHBhcmVudC4KICAgICAgICAgICAgaWYgKCFoZWFkKSB7CiAgICAgICAgICAgICAgICBoZWFkID0gc2NyaXB0LnBhcmVudE5vZGU7CiAgICAgICAgICAgIH0KCiAgICAgICAgICAgIC8vTG9vayBmb3IgYSBkYXRhLW1haW4gYXR0cmlidXRlIHRvIHNldCBtYWluIHNjcmlwdCBmb3IgdGhlIHBhZ2UKICAgICAgICAgICAgLy90byBsb2FkLiBJZiBpdCBpcyB0aGVyZSwgdGhlIHBhdGggdG8gZGF0YSBtYWluIGJlY29tZXMgdGhlCiAgICAgICAgICAgIC8vYmFzZVVybCwgaWYgaXQgaXMgbm90IGFscmVhZHkgc2V0LgogICAgICAgICAgICBkYXRhTWFpbiA9IHNjcmlwdC5nZXRBdHRyaWJ1dGUoJ2RhdGEtbWFpbicpOwogICAgICAgICAgICBpZiAoZGF0YU1haW4pIHsKICAgICAgICAgICAgICAgIC8vUHJlc2VydmUgZGF0YU1haW4gaW4gY2FzZSBpdCBpcyBhIHBhdGggKGkuZS4gY29udGFpbnMgJz8nKQogICAgICAgICAgICAgICAgbWFpblNjcmlwdCA9IGRhdGFNYWluOwoKICAgICAgICAgICAgICAgIC8vU2V0IGZpbmFsIGJhc2VVcmwgaWYgdGhlcmUgaXMgbm90IGFscmVhZHkgYW4gZXhwbGljaXQgb25lLgogICAgICAgICAgICAgICAgaWYgKCFjZmcuYmFzZVVybCkgewogICAgICAgICAgICAgICAgICAgIC8vUHVsbCBvZmYgdGhlIGRpcmVjdG9yeSBvZiBkYXRhLW1haW4gZm9yIHVzZSBhcyB0aGUKICAgICAgICAgICAgICAgICAgICAvL2Jhc2VVcmwuCiAgICAgICAgICAgICAgICAgICAgc3JjID0gbWFpblNjcmlwdC5zcGxpdCgnLycpOwogICAgICAgICAgICAgICAgICAgIG1haW5TY3JpcHQgPSBzcmMucG9wKCk7CiAgICAgICAgICAgICAgICAgICAgc3ViUGF0aCA9IHNyYy5sZW5ndGggPyBzcmMuam9pbignLycpICArICcvJyA6ICcuLyc7CgogICAgICAgICAgICAgICAgICAgIGNmZy5iYXNlVXJsID0gc3ViUGF0aDsKICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICAvL1N0cmlwIG9mZiBhbnkgdHJhaWxpbmcgLmpzIHNpbmNlIG1haW5TY3JpcHQgaXMgbm93CiAgICAgICAgICAgICAgICAvL2xpa2UgYSBtb2R1bGUgbmFtZS4KICAgICAgICAgICAgICAgIG1haW5TY3JpcHQgPSBtYWluU2NyaXB0LnJlcGxhY2UoanNTdWZmaXhSZWdFeHAsICcnKTsKCiAgICAgICAgICAgICAgICAvL0lmIG1haW5TY3JpcHQgaXMgc3RpbGwgYSBwYXRoLCBmYWxsIGJhY2sgdG8gZGF0YU1haW4KICAgICAgICAgICAgICAgIGlmIChyZXEuanNFeHRSZWdFeHAudGVzdChtYWluU2NyaXB0KSkgewogICAgICAgICAgICAgICAgICAgIG1haW5TY3JpcHQgPSBkYXRhTWFpbjsKICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICAvL1B1dCB0aGUgZGF0YS1tYWluIHNjcmlwdCBpbiB0aGUgZmlsZXMgdG8gbG9hZC4KICAgICAgICAgICAgICAgIGNmZy5kZXBzID0gY2ZnLmRlcHMgPyBjZmcuZGVwcy5jb25jYXQobWFpblNjcmlwdCkgOiBbbWFpblNjcmlwdF07CgogICAgICAgICAgICAgICAgcmV0dXJuIHRydWU7CiAgICAgICAgICAgIH0KICAgICAgICB9KTsKICAgIH0KCiAgICAvKioKICAgICAqIFRoZSBmdW5jdGlvbiB0aGF0IGhhbmRsZXMgZGVmaW5pdGlvbnMgb2YgbW9kdWxlcy4gRGlmZmVycyBmcm9tCiAgICAgKiByZXF1aXJlKCkgaW4gdGhhdCBhIHN0cmluZyBmb3IgdGhlIG1vZHVsZSBzaG91bGQgYmUgdGhlIGZpcnN0IGFyZ3VtZW50LAogICAgICogYW5kIHRoZSBmdW5jdGlvbiB0byBleGVjdXRlIGFmdGVyIGRlcGVuZGVuY2llcyBhcmUgbG9hZGVkIHNob3VsZAogICAgICogcmV0dXJuIGEgdmFsdWUgdG8gZGVmaW5lIHRoZSBtb2R1bGUgY29ycmVzcG9uZGluZyB0byB0aGUgZmlyc3QgYXJndW1lbnQncwogICAgICogbmFtZS4KICAgICAqLwogICAgZGVmaW5lID0gZnVuY3Rpb24gKG5hbWUsIGRlcHMsIGNhbGxiYWNrKSB7CiAgICAgICAgdmFyIG5vZGUsIGNvbnRleHQ7CgogICAgICAgIC8vQWxsb3cgZm9yIGFub255bW91cyBtb2R1bGVzCiAgICAgICAgaWYgKHR5cGVvZiBuYW1lICE9PSAnc3RyaW5nJykgewogICAgICAgICAgICAvL0FkanVzdCBhcmdzIGFwcHJvcHJpYXRlbHkKICAgICAgICAgICAgY2FsbGJhY2sgPSBkZXBzOwogICAgICAgICAgICBkZXBzID0gbmFtZTsKICAgICAgICAgICAgbmFtZSA9IG51bGw7CiAgICAgICAgfQoKICAgICAgICAvL1RoaXMgbW9kdWxlIG1heSBub3QgaGF2ZSBkZXBlbmRlbmNpZXMKICAgICAgICBpZiAoIWlzQXJyYXkoZGVwcykpIHsKICAgICAgICAgICAgY2FsbGJhY2sgPSBkZXBzOwogICAgICAgICAgICBkZXBzID0gbnVsbDsKICAgICAgICB9CgogICAgICAgIC8vSWYgbm8gbmFtZSwgYW5kIGNhbGxiYWNrIGlzIGEgZnVuY3Rpb24sIHRoZW4gZmlndXJlIG91dCBpZiBpdCBhCiAgICAgICAgLy9Db21tb25KUyB0aGluZyB3aXRoIGRlcGVuZGVuY2llcy4KICAgICAgICBpZiAoIWRlcHMgJiYgaXNGdW5jdGlvbihjYWxsYmFjaykpIHsKICAgICAgICAgICAgZGVwcyA9IFtdOwogICAgICAgICAgICAvL1JlbW92ZSBjb21tZW50cyBmcm9tIHRoZSBjYWxsYmFjayBzdHJpbmcsCiAgICAgICAgICAgIC8vbG9vayBmb3IgcmVxdWlyZSBjYWxscywgYW5kIHB1bGwgdGhlbSBpbnRvIHRoZSBkZXBlbmRlbmNpZXMsCiAgICAgICAgICAgIC8vYnV0IG9ubHkgaWYgdGhlcmUgYXJlIGZ1bmN0aW9uIGFyZ3MuCiAgICAgICAgICAgIGlmIChjYWxsYmFjay5sZW5ndGgpIHsKICAgICAgICAgICAgICAgIGNhbGxiYWNrCiAgICAgICAgICAgICAgICAgICAgLnRvU3RyaW5nKCkKICAgICAgICAgICAgICAgICAgICAucmVwbGFjZShjb21tZW50UmVnRXhwLCAnJykKICAgICAgICAgICAgICAgICAgICAucmVwbGFjZShjanNSZXF1aXJlUmVnRXhwLCBmdW5jdGlvbiAobWF0Y2gsIGRlcCkgewogICAgICAgICAgICAgICAgICAgICAgICBkZXBzLnB1c2goZGVwKTsKICAgICAgICAgICAgICAgICAgICB9KTsKCiAgICAgICAgICAgICAgICAvL01heSBiZSBhIENvbW1vbkpTIHRoaW5nIGV2ZW4gd2l0aG91dCByZXF1aXJlIGNhbGxzLCBidXQgc3RpbGwKICAgICAgICAgICAgICAgIC8vY291bGQgdXNlIGV4cG9ydHMsIGFuZCBtb2R1bGUuIEF2b2lkIGRvaW5nIGV4cG9ydHMgYW5kIG1vZHVsZQogICAgICAgICAgICAgICAgLy93b3JrIHRob3VnaCBpZiBpdCBqdXN0IG5lZWRzIHJlcXVpcmUuCiAgICAgICAgICAgICAgICAvL1JFUVVJUkVTIHRoZSBmdW5jdGlvbiB0byBleHBlY3QgdGhlIENvbW1vbkpTIHZhcmlhYmxlcyBpbiB0aGUKICAgICAgICAgICAgICAgIC8vb3JkZXIgbGlzdGVkIGJlbG93LgogICAgICAgICAgICAgICAgZGVwcyA9IChjYWxsYmFjay5sZW5ndGggPT09IDEgPyBbJ3JlcXVpcmUnXSA6IFsncmVxdWlyZScsICdleHBvcnRzJywgJ21vZHVsZSddKS5jb25jYXQoZGVwcyk7CiAgICAgICAgICAgIH0KICAgICAgICB9CgogICAgICAgIC8vSWYgaW4gSUUgNi04IGFuZCBoaXQgYW4gYW5vbnltb3VzIGRlZmluZSgpIGNhbGwsIGRvIHRoZSBpbnRlcmFjdGl2ZQogICAgICAgIC8vd29yay4KICAgICAgICBpZiAodXNlSW50ZXJhY3RpdmUpIHsKICAgICAgICAgICAgbm9kZSA9IGN1cnJlbnRseUFkZGluZ1NjcmlwdCB8fCBnZXRJbnRlcmFjdGl2ZVNjcmlwdCgpOwogICAgICAgICAgICBpZiAobm9kZSkgewogICAgICAgICAgICAgICAgaWYgKCFuYW1lKSB7CiAgICAgICAgICAgICAgICAgICAgbmFtZSA9IG5vZGUuZ2V0QXR0cmlidXRlKCdkYXRhLXJlcXVpcmVtb2R1bGUnKTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIGNvbnRleHQgPSBjb250ZXh0c1tub2RlLmdldEF0dHJpYnV0ZSgnZGF0YS1yZXF1aXJlY29udGV4dCcpXTsKICAgICAgICAgICAgfQogICAgICAgIH0KCiAgICAgICAgLy9BbHdheXMgc2F2ZSBvZmYgZXZhbHVhdGluZyB0aGUgZGVmIGNhbGwgdW50aWwgdGhlIHNjcmlwdCBvbmxvYWQgaGFuZGxlci4KICAgICAgICAvL1RoaXMgYWxsb3dzIG11bHRpcGxlIG1vZHVsZXMgdG8gYmUgaW4gYSBmaWxlIHdpdGhvdXQgcHJlbWF0dXJlbHkKICAgICAgICAvL3RyYWNpbmcgZGVwZW5kZW5jaWVzLCBhbmQgYWxsb3dzIGZvciBhbm9ueW1vdXMgbW9kdWxlIHN1cHBvcnQsCiAgICAgICAgLy93aGVyZSB0aGUgbW9kdWxlIG5hbWUgaXMgbm90IGtub3duIHVudGlsIHRoZSBzY3JpcHQgb25sb2FkIGV2ZW50CiAgICAgICAgLy9vY2N1cnMuIElmIG5vIGNvbnRleHQsIHVzZSB0aGUgZ2xvYmFsIHF1ZXVlLCBhbmQgZ2V0IGl0IHByb2Nlc3NlZAogICAgICAgIC8vaW4gdGhlIG9uc2NyaXB0IGxvYWQgY2FsbGJhY2suCiAgICAgICAgaWYgKGNvbnRleHQpIHsKICAgICAgICAgICAgY29udGV4dC5kZWZRdWV1ZS5wdXNoKFtuYW1lLCBkZXBzLCBjYWxsYmFja10pOwogICAgICAgICAgICBjb250ZXh0LmRlZlF1ZXVlTWFwW25hbWVdID0gdHJ1ZTsKICAgICAgICB9IGVsc2UgewogICAgICAgICAgICBnbG9iYWxEZWZRdWV1ZS5wdXNoKFtuYW1lLCBkZXBzLCBjYWxsYmFja10pOwogICAgICAgIH0KICAgIH07CgogICAgZGVmaW5lLmFtZCA9IHsKICAgICAgICBqUXVlcnk6IHRydWUKICAgIH07CgogICAgLyoqCiAgICAgKiBFeGVjdXRlcyB0aGUgdGV4dC4gTm9ybWFsbHkganVzdCB1c2VzIGV2YWwsIGJ1dCBjYW4gYmUgbW9kaWZpZWQKICAgICAqIHRvIHVzZSBhIGJldHRlciwgZW52aXJvbm1lbnQtc3BlY2lmaWMgY2FsbC4gT25seSB1c2VkIGZvciB0cmFuc3BpbGluZwogICAgICogbG9hZGVyIHBsdWdpbnMsIG5vdCBmb3IgcGxhaW4gSlMgbW9kdWxlcy4KICAgICAqIEBwYXJhbSB7U3RyaW5nfSB0ZXh0IHRoZSB0ZXh0IHRvIGV4ZWN1dGUvZXZhbHVhdGUuCiAgICAgKi8KICAgIHJlcS5leGVjID0gZnVuY3Rpb24gKHRleHQpIHsKICAgICAgICAvKmpzbGludCBldmlsOiB0cnVlICovCiAgICAgICAgcmV0dXJuIGV2YWwodGV4dCk7CiAgICB9OwoKICAgIC8vU2V0IHVwIHdpdGggY29uZmlnIGluZm8uCiAgICByZXEoY2ZnKTsKfSh0aGlzKSk7Cg==",
"ok": true,
"headers": [
[
"content-type",
"application/javascript"
]
],
"status": 200,
"status_text": ""
}
},
"base_uri": "https://localhost:8080/",
"height": 1000
}
},
"source": [
"# You would expect it to perform worse, the other strategy looked into the future\n",
"configure_plotly_browser_state()\n",
"# do LASSO subset selection at each timestep\n",
"backtestmodel = BacktestModel(X, Y,\n",
" create_model=LinearRegression,\n",
" coef_dict_param=\"timestep\", # instead of feeding paper predictors, at each time step perform an online recalculation\n",
" startindex=FIRST_TRAIN_MONTHS,\n",
" fit_missing='mean',\n",
" scaler = None)\n",
"backtestmodel.gen_predictions(verbose=False)\n",
"backtestmodel.gen_returns(calc_returns, verbose=False)\n",
"backtestmodel.report_returns(start_date=start_date_str, freq='M')\n",
"backtestmodel.evaluate_predictions()\n",
"backtestmodel.evaluate_quantiles(chart=True, verbose=True)\n",
"perf_LASSO_each_timestep = backtestmodel.cumulative_return\n",
"mychart([perf_LASSO_each_timestep],[\"LASSO each timestep\"], title=\"LASSO each timestep\")\n",
"\n"
],
"execution_count": null,
"outputs": [
{
"output_type": "display_data",
"data": {
"text/html": [
"\n",
" <script src=\"/static/components/requirejs/require.js\"></script>\n",
" <script>\n",
" requirejs.config({\n",
" paths: {\n",
" base: '/static/base',\n",
" plotly: 'https://cdn.plot.ly/plotly-latest.min.js?noext',\n",
" },\n",
" });\n",
" </script>\n",
" "
],
"text/plain": [
"<IPython.core.display.HTML object>"
]
},
"metadata": {}
},
{
"output_type": "stream",
"name": "stdout",
"text": [
"................................................................................\n",
"15:04:12 Still training step 80 of 563\n",
"................................................................................\n",
"15:05:00 Still training step 160 of 563\n",
"................................................................................\n",
"15:05:49 Still training step 240 of 563\n",
"................................................................................\n",
"15:06:39 Still training step 320 of 563\n",
"................................................................................\n",
"15:07:33 Still training step 400 of 563\n",
"................................................................................\n",
"15:08:25 Still training step 480 of 563\n",
"................................................................................\n",
"15:09:17 Still training step 560 of 563\n",
"...\n",
"Mean return: 3.488%\n",
"Monthly Sharpe ratio: 0.653\n",
"OOS MSE across all predictions: 41.6397\n",
"In-sample MSE: 35.6311\n",
"Variance: 39.4097\n",
"R-squared: -0.0566\n",
"Avg rank correlation (Kendall's tau): 0.0322 (Expected: 0)\n",
"5-quintile accuracy: 0.2172 (Expected: 0.2)\n",
"Long/short/flat accuracy: 0.4584 (Expected: 0.44)\n",
"Confusion matrix for quantile 0\n",
"[[10902 2610]\n",
" [ 2606 772]]\n",
"Chi-square: 21.1431 (p-value: 0.00009831)\n",
"Confusion matrix for quantile 1\n",
"[[10842 2670]\n",
" [ 2670 708]]\n",
"Chi-square: 2.4278 (p-value: 0.48847290)\n",
"Confusion matrix for quantile 2\n",
"[[10853 2659]\n",
" [ 2653 725]]\n",
"Chi-square: 5.3864 (p-value: 0.14559227)\n",
"Confusion matrix for quantile 3\n",
"[[10821 2691]\n",
" [ 2696 682]]\n",
"Chi-square: 0.1359 (p-value: 0.98720619)\n",
"Confusion matrix for quantile 4\n",
"[[10920 2592]\n",
" [ 2597 781]]\n",
"Chi-square: 26.1919 (p-value: 0.00000869)\n",
"Excess true positive in quintiles 1 + 5: 201.800000\n"
]
},
{
"output_type": "display_data",
"data": {
"image/png": "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\n",
"text/plain": [
"<Figure size 720x720 with 2 Axes>"
]
},
"metadata": {
"needs_background": "light"
}
},
{
"output_type": "display_data",
"data": {
"text/html": [
"<div id=\"a8f0bb3b-b1b2-4111-9aa4-dbdb44a489d2\" style=\"height: 600px; width: 900px;\" class=\"plotly-graph-div\"></div><script type=\"text/javascript\">require([\"plotly\"], function(Plotly) { window.PLOTLYENV=window.PLOTLYENV || {};window.PLOTLYENV.BASE_URL=\"https://plot.ly\";Plotly.newPlot(\"a8f0bb3b-b1b2-4111-9aa4-dbdb44a489d2\", [{\"type\": \"scatter\", \"x\": [1970.0, 1970.081850533808, 1970.1637010676156, 1970.2455516014236, 1970.3274021352313, 1970.4092526690392, 1970.491103202847, 1970.5729537366549, 1970.6548042704626, 1970.7366548042705, 1970.8185053380782, 1970.9003558718862, 1970.982206405694, 1971.0640569395018, 1971.1459074733095, 1971.2277580071175, 1971.3096085409252, 1971.3914590747331, 1971.4733096085408, 1971.5551601423488, 1971.6370106761565, 1971.7188612099644, 1971.8007117437724, 1971.88256227758, 1971.964412811388, 1972.0462633451957, 1972.1281138790036, 1972.2099644128114, 1972.2918149466193, 1972.373665480427, 1972.455516014235, 1972.5373665480427, 1972.6192170818506, 1972.7010676156583, 1972.7829181494662, 1972.864768683274, 1972.946619217082, 1973.0284697508896, 1973.1103202846975, 1973.1921708185052, 1973.2740213523132, 1973.355871886121, 1973.4377224199288, 1973.5195729537365, 1973.6014234875445, 1973.6832740213524, 1973.7651245551601, 1973.846975088968, 1973.9288256227758, 1974.0106761565837, 1974.0925266903914, 1974.1743772241994, 1974.256227758007, 1974.338078291815, 1974.4199288256227, 1974.5017793594307, 1974.5836298932384, 1974.6654804270463, 1974.747330960854, 1974.829181494662, 1974.9110320284697, 1974.9928825622776, 1975.0747330960853, 1975.1565836298932, 1975.238434163701, 1975.320284697509, 1975.4021352313168, 1975.4839857651245, 1975.5658362989325, 1975.6476868327402, 1975.7295373665481, 1975.8113879003558, 1975.8932384341638, 1975.9750889679715, 1976.0569395017794, 1976.1387900355871, 1976.220640569395, 1976.3024911032028, 1976.3843416370107, 1976.4661921708184, 1976.5480427046264, 1976.629893238434, 1976.711743772242, 1976.7935943060497, 1976.8754448398577, 1976.9572953736654, 1977.0391459074733, 1977.1209964412812, 1977.202846975089, 1977.284697508897, 1977.3665480427046, 1977.4483985765125, 1977.5302491103203, 1977.6120996441282, 1977.693950177936, 1977.7758007117438, 1977.8576512455516, 1977.9395017793595, 1978.0213523131672, 1978.1032028469751, 1978.1850533807828, 1978.2669039145908, 1978.3487544483985, 1978.4306049822064, 1978.5124555160141, 1978.594306049822, 1978.6761565836298, 1978.7580071174377, 1978.8398576512454, 1978.9217081850534, 1979.0035587188613, 1979.085409252669, 1979.167259786477, 1979.2491103202847, 1979.3309608540926, 1979.4128113879003, 1979.4946619217083, 1979.576512455516, 1979.658362989324, 1979.7402135231316, 1979.8220640569396, 1979.9039145907473, 1979.9857651245552, 1980.067615658363, 1980.1494661921708, 1980.2313167259786, 1980.3131672597865, 1980.3950177935942, 1980.4768683274021, 1980.5587188612099, 1980.6405693950178, 1980.7224199288257, 1980.8042704626334, 1980.8861209964414, 1980.967971530249, 1981.049822064057, 1981.1316725978647, 1981.2135231316727, 1981.2953736654804, 1981.3772241992883, 1981.459074733096, 1981.540925266904, 1981.6227758007117, 1981.7046263345196, 1981.7864768683273, 1981.8683274021353, 1981.950177935943, 1982.032028469751, 1982.1138790035586, 1982.1957295373666, 1982.2775800711743, 1982.3594306049822, 1982.4412811387901, 1982.5231316725979, 1982.6049822064058, 1982.6868327402135, 1982.7686832740214, 1982.8505338078292, 1982.932384341637, 1983.0142348754448, 1983.0960854092527, 1983.1779359430604, 1983.2597864768684, 1983.341637010676, 1983.423487544484, 1983.5053380782917, 1983.5871886120997, 1983.6690391459074, 1983.7508896797153, 1983.832740213523, 1983.914590747331, 1983.9964412811387, 1984.0782918149466, 1984.1601423487546, 1984.2419928825623, 1984.3238434163702, 1984.405693950178, 1984.4875444839859, 1984.5693950177936, 1984.6512455516015, 1984.7330960854092, 1984.8149466192172, 1984.8967971530249, 1984.9786476868328, 1985.0604982206405, 1985.1423487544484, 1985.2241992882562, 1985.306049822064, 1985.3879003558718, 1985.4697508896797, 1985.5516014234875, 1985.6334519572954, 1985.715302491103, 1985.797153024911, 1985.8790035587188, 1985.9608540925267, 1986.0427046263346, 1986.1245551601423, 1986.2064056939503, 1986.288256227758, 1986.370106761566, 1986.4519572953736, 1986.5338078291816, 1986.6156583629893, 1986.6975088967972, 1986.779359430605, 1986.8612099644129, 1986.9430604982206, 1987.0249110320285, 1987.1067615658362, 1987.1886120996442, 1987.2704626334519, 1987.3523131672598, 1987.4341637010675, 1987.5160142348755, 1987.5978647686832, 1987.679715302491, 1987.761565836299, 1987.8434163701068, 1987.9252669039147, 1988.0071174377224, 1988.0889679715303, 1988.170818505338, 1988.252669039146, 1988.3345195729537, 1988.4163701067616, 1988.4982206405693, 1988.5800711743773, 1988.661921708185, 1988.743772241993, 1988.8256227758006, 1988.9074733096086, 1988.9893238434163, 1989.0711743772242, 1989.153024911032, 1989.2348754448399, 1989.3167259786476, 1989.3985765124555, 1989.4804270462632, 1989.5622775800712, 1989.644128113879, 1989.7259786476868, 1989.8078291814948, 1989.8896797153025, 1989.9715302491104, 1990.053380782918, 1990.135231316726, 1990.2170818505338, 1990.2989323843417, 1990.3807829181494, 1990.4626334519573, 1990.544483985765, 1990.626334519573, 1990.7081850533807, 1990.7900355871886, 1990.8718861209964, 1990.9537366548043, 1991.035587188612, 1991.11743772242, 1991.1992882562276, 1991.2811387900356, 1991.3629893238435, 1991.4448398576512, 1991.5266903914592, 1991.6085409252669, 1991.6903914590748, 1991.7722419928825, 1991.8540925266905, 1991.9359430604982, 1992.017793594306, 1992.0996441281138, 1992.1814946619218, 1992.2633451957295, 1992.3451957295374, 1992.4270462633451, 1992.508896797153, 1992.5907473309608, 1992.6725978647687, 1992.7544483985764, 1992.8362989323844, 1992.918149466192, 1993.0, 1993.081850533808, 1993.1637010676156, 1993.2455516014236, 1993.3274021352313, 1993.4092526690392, 1993.491103202847, 1993.5729537366549, 1993.6548042704626, 1993.7366548042705, 1993.8185053380782, 1993.9003558718862, 1993.982206405694, 1994.0640569395018, 1994.1459074733095, 1994.2277580071175, 1994.3096085409252, 1994.3914590747331, 1994.4733096085408, 1994.5551601423488, 1994.6370106761565, 1994.7188612099644, 1994.8007117437724, 1994.88256227758, 1994.964412811388, 1995.0462633451957, 1995.1281138790036, 1995.2099644128114, 1995.2918149466193, 1995.373665480427, 1995.455516014235, 1995.5373665480427, 1995.6192170818506, 1995.7010676156583, 1995.7829181494662, 1995.864768683274, 1995.946619217082, 1996.0284697508896, 1996.1103202846975, 1996.1921708185052, 1996.2740213523132, 1996.355871886121, 1996.4377224199288, 1996.5195729537368, 1996.6014234875445, 1996.6832740213524, 1996.7651245551601, 1996.846975088968, 1996.9288256227758, 1997.0106761565837, 1997.0925266903914, 1997.1743772241994, 1997.256227758007, 1997.338078291815, 1997.4199288256227, 1997.5017793594307, 1997.5836298932384, 1997.6654804270463, 1997.747330960854, 1997.829181494662, 1997.9110320284697, 1997.9928825622776, 1998.0747330960853, 1998.1565836298932, 1998.238434163701, 1998.320284697509, 1998.4021352313168, 1998.4839857651245, 1998.5658362989325, 1998.6476868327402, 1998.7295373665481, 1998.8113879003558, 1998.8932384341638, 1998.9750889679715, 1999.0569395017794, 1999.1387900355871, 1999.220640569395, 1999.3024911032028, 1999.3843416370107, 1999.4661921708184, 1999.5480427046264, 1999.629893238434, 1999.711743772242, 1999.7935943060497, 1999.8754448398577, 1999.9572953736654, 2000.0391459074733, 2000.1209964412812, 2000.202846975089, 2000.284697508897, 2000.3665480427046, 2000.4483985765125, 2000.5302491103203, 2000.6120996441282, 2000.693950177936, 2000.7758007117438, 2000.8576512455516, 2000.9395017793595, 2001.0213523131672, 2001.1032028469751, 2001.1850533807828, 2001.2669039145908, 2001.3487544483985, 2001.4306049822064, 2001.5124555160141, 2001.594306049822, 2001.6761565836298, 2001.7580071174377, 2001.8398576512454, 2001.9217081850534, 2002.0035587188613, 2002.085409252669, 2002.167259786477, 2002.2491103202847, 2002.3309608540926, 2002.4128113879003, 2002.4946619217083, 2002.576512455516, 2002.658362989324, 2002.7402135231316, 2002.8220640569396, 2002.9039145907473, 2002.9857651245552, 2003.067615658363, 2003.1494661921708, 2003.2313167259786, 2003.3131672597865, 2003.3950177935942, 2003.4768683274021, 2003.5587188612099, 2003.6405693950178, 2003.7224199288257, 2003.8042704626334, 2003.8861209964414, 2003.967971530249, 2004.049822064057, 2004.1316725978647, 2004.2135231316727, 2004.2953736654804, 2004.3772241992883, 2004.459074733096, 2004.540925266904, 2004.6227758007117, 2004.7046263345196, 2004.7864768683273, 2004.8683274021353, 2004.950177935943, 2005.032028469751, 2005.1138790035586, 2005.1957295373666, 2005.2775800711743, 2005.3594306049822, 2005.4412811387901, 2005.5231316725979, 2005.6049822064058, 2005.6868327402135, 2005.7686832740214, 2005.8505338078292, 2005.932384341637, 2006.0142348754448, 2006.0960854092527, 2006.1779359430604, 2006.2597864768684, 2006.341637010676, 2006.423487544484, 2006.5053380782917, 2006.5871886120997, 2006.6690391459074, 2006.7508896797153, 2006.832740213523, 2006.914590747331, 2006.9964412811387, 2007.0782918149466, 2007.1601423487546, 2007.2419928825623, 2007.3238434163702, 2007.405693950178, 2007.4875444839859, 2007.5693950177936, 2007.6512455516015, 2007.7330960854092, 2007.8149466192172, 2007.8967971530249, 2007.9786476868328, 2008.0604982206405, 2008.1423487544484, 2008.2241992882562, 2008.306049822064, 2008.3879003558718, 2008.4697508896797, 2008.5516014234875, 2008.6334519572954, 2008.715302491103, 2008.797153024911, 2008.879003558719, 2008.9608540925267, 2009.0427046263346, 2009.1245551601423, 2009.2064056939503, 2009.288256227758, 2009.370106761566, 2009.4519572953736, 2009.5338078291816, 2009.6156583629893, 2009.6975088967972, 2009.779359430605, 2009.8612099644129, 2009.9430604982206, 2010.0249110320285, 2010.1067615658362, 2010.1886120996442, 2010.2704626334519, 2010.3523131672598, 2010.4341637010675, 2010.5160142348755, 2010.5978647686832, 2010.679715302491, 2010.761565836299, 2010.8434163701068, 2010.9252669039147, 2011.0071174377224, 2011.0889679715303, 2011.170818505338, 2011.252669039146, 2011.3345195729537, 2011.4163701067616, 2011.4982206405693, 2011.5800711743773, 2011.661921708185, 2011.743772241993, 2011.8256227758006, 2011.9074733096086, 2011.9893238434163, 2012.0711743772242, 2012.153024911032, 2012.2348754448399, 2012.3167259786476, 2012.3985765124555, 2012.4804270462632, 2012.5622775800712, 2012.644128113879, 2012.7259786476868, 2012.8078291814948, 2012.8896797153025, 2012.9715302491104, 2013.053380782918, 2013.135231316726, 2013.2170818505338, 2013.2989323843417, 2013.3807829181494, 2013.4626334519573, 2013.544483985765, 2013.626334519573, 2013.7081850533807, 2013.7900355871886, 2013.8718861209964, 2013.9537366548043, 2014.035587188612, 2014.11743772242, 2014.1992882562276, 2014.2811387900356, 2014.3629893238435, 2014.4448398576512, 2014.5266903914592, 2014.6085409252669, 2014.6903914590748, 2014.7722419928825, 2014.8540925266905, 2014.9359430604982, 2015.017793594306, 2015.0996441281138, 2015.1814946619218, 2015.2633451957295, 2015.3451957295374, 2015.4270462633451, 2015.508896797153, 2015.5907473309608, 2015.6725978647687, 2015.7544483985764, 2015.8362989323844, 2015.918149466192, 2016.0], \"y\": [100.0, 100.27916666666667, 99.84963756944445, 100.79654496572803, 101.60207735424581, 101.67658554430557, 100.10314038300743, 102.3796526338843, 105.57133830474565, 104.18747401180096, 103.84539180546221, 103.77443078772848, 104.76201745405835, 105.36003397035863, 106.95448248444339, 104.93928177696567, 105.14828584650479, 104.72769270311876, 105.87620639976296, 104.27482877796655, 104.3234903647296, 104.95117003175736, 105.40858221447908, 105.12046542309284, 108.21889114143848, 109.21270129175402, 110.13463851182526, 111.28003875234825, 113.99712636521808, 114.77705670476679, 115.83396210192318, 117.71529870306192, 118.11945456194243, 118.68052197111167, 118.71612612770302, 117.81586217123458, 118.16734616004543, 120.59568512363437, 121.79762211869993, 120.85978042838595, 122.78044377236037, 126.31038153081573, 125.24411139339308, 124.36114040806967, 125.68973192476255, 125.35979637846006, 125.62305195085483, 126.88032932912965, 126.28187710912724, 125.85462342490803, 123.88814493389384, 125.98908139173113, 125.30244089814619, 126.74237478146739, 125.54994027206507, 128.27542022547115, 131.09747947043152, 134.1367560361544, 134.44191715613664, 136.8461867746122, 128.5054116906996, 131.98897922594827, 132.21116067431197, 134.83334869435248, 135.58167377960612, 135.26983592991303, 137.86363503386912, 139.275588429341, 139.42995220651684, 137.26878794731584, 138.2983038569207, 137.3325207016532, 136.16633871336165, 138.87604885375754, 137.93284902195913, 136.7765119709917, 140.02951334736844, 142.60605639296003, 142.6488382098779, 145.31280526344736, 146.32757302020377, 145.27035630513282, 144.01255713679086, 142.38881555507353, 142.1206499524448, 143.25169345831637, 143.8808071454208, 146.0294271987924, 143.89739756169004, 143.32900284132137, 144.5019118479062, 145.2244214071457, 145.2510458844037, 146.03298068141473, 146.19118307715294, 147.10244145166718, 146.93695120503406, 146.13492034637324, 145.17408324509583, 148.11022907872788, 150.41951440044704, 149.74262658564504, 149.9872062090683, 152.54198828816274, 151.58987204459746, 152.21518026678143, 151.329795301563, 153.17601880424206, 155.26048912680312, 155.29801041167545, 155.51413347616503, 156.6467947483164, 157.1167351325614, 161.5736131858217, 165.0178240402328, 164.88856007806794, 165.93697650589763, 163.1284931785353, 166.11782281603195, 169.4609440002046, 173.82879983180987, 175.7655423766026, 170.7313243003661, 173.6807079276549, 172.21455328489895, 170.42352193073603, 172.0198222528206, 174.28474991248274, 177.55549371917368, 178.81761735369412, 179.25125007577682, 175.80663855348732, 177.27901915137275, 179.6693312595971, 181.98407114399157, 182.76508611598453, 183.90584486182513, 181.23767756262149, 183.22827138785095, 183.9016352852013, 184.09932954313288, 184.43377665846955, 184.9517281812521, 185.9674214218475, 184.49362960707933, 186.97199403146777, 191.39076549041147, 192.61885623564163, 193.91261288669105, 192.2029500164067, 194.14259812032228, 194.25423011424147, 195.3355786618774, 195.69694948240186, 203.80695723053503, 199.42340925876826, 204.25610320980576, 198.24586737285722, 200.95027141360197, 202.36194707028253, 201.456377357143, 205.49054131371975, 203.78154497846066, 210.3178380336448, 210.44052343916442, 212.08897420610455, 207.32580932705912, 207.50721941022027, 211.6314253959984, 215.42315510101005, 212.8775714848998, 210.9261937462882, 212.79289056094285, 212.6474820857262, 215.02558976038492, 211.01357063143905, 207.7024160189474, 209.0524817230705, 209.93921266637923, 210.9399229134223, 209.97663059878434, 207.6703872727077, 215.52378908473725, 214.4856828339791, 215.70110170337165, 217.91922803255468, 211.35985926877478, 211.72973902249512, 212.71428230894975, 215.56465369188967, 218.00053427860803, 221.0761584830554, 220.8403439140068, 224.3314616840474, 225.3596475500993, 226.71368343246283, 230.03314961405312, 234.9079354429576, 236.28410443142758, 236.6877564431646, 241.12959667241464, 235.96942330362495, 238.10494658452276, 238.1823306921627, 233.21424424447534, 238.93771048864184, 235.96094484547083, 239.36861415728083, 240.95642596452413, 242.33188556273834, 239.3835142883917, 237.46645131146548, 237.7177699724368, 232.0719729355914, 229.2155537353758, 230.6042179650893, 231.37482039345593, 227.90612621105737, 229.32674106443963, 232.87366132623632, 233.86337438687286, 232.4426543874726, 230.29449685650837, 232.44775040211672, 232.8874640632941, 231.25725181485103, 230.45556000855956, 231.5329397515996, 232.2815629234631, 232.16348646231035, 233.9975780053626, 232.56824279971315, 236.64981546084812, 238.0992955805458, 238.06358068620872, 231.9215403045045, 229.1442798593581, 227.86680049914216, 229.34033914236994, 227.22085217479585, 231.9754485065535, 235.07812013032864, 233.03881743819807, 233.22913247243923, 231.9036135695542, 230.59142562277316, 229.60180408780872, 230.26573597129598, 240.84452765804397, 246.325747699995, 245.13106782365003, 247.96641717481026, 249.7724392465668, 253.1714258573138, 257.63568199993114, 256.6931647966147, 252.1860606447275, 251.56400169513716, 246.96247683079693, 246.43562354689124, 246.97778191869438, 247.6055171144044, 252.12638118105156, 255.60992734770304, 265.2272508641604, 264.0558305061771, 264.7753826443064, 259.77774729689514, 260.96839530533924, 260.29857642405557, 257.6934215050115, 258.3440973943116, 256.08358654211133, 258.07250239758844, 265.1157311088559, 264.4198023146952, 268.2803314284897, 269.0382233647752, 270.33857477770493, 270.7958975333705, 273.2646534658831, 270.9419039114231, 274.0532201080059, 276.373537371587, 274.0312716423628, 273.7458224010687, 275.16017581680757, 273.31430963736983, 271.24167612261977, 270.0549937895833, 267.49172180686384, 268.0177888597507, 264.27670722358334, 262.0435690475441, 263.32539883946833, 258.9059208956126, 258.5909186918563, 256.5825292233495, 260.17040825698933, 256.6711162659328, 254.37390977535276, 255.46347802222385, 256.77911493403826, 254.10647231276647, 255.06572424574722, 256.8193010999367, 255.37683269209202, 253.1827200712125, 254.35158029554125, 258.88963640731424, 258.0417728480803, 254.72163537076833, 257.6615475790059, 258.3314676027113, 258.5553548746336, 261.48349426858886, 263.8259505714116, 265.39351642772345, 268.8170927896411, 269.0993507370702, 271.21850812412464, 270.1991785644248, 270.899444768871, 266.09549461496965, 267.4414943252304, 274.92539880809807, 274.5038465299256, 276.42537345563505, 277.77525069601006, 276.39563361755324, 273.32073219355794, 277.58681328854567, 277.61688519331864, 281.85054269251674, 293.4839238421503, 299.9479072647737, 304.7920659670998, 301.37331496050217, 299.1682668727078, 304.4062046118708, 297.1613369421082, 301.8292462765739, 303.84647173918904, 314.5241431668903, 307.20359373468096, 301.44096632220794, 305.69881997150907, 305.07977986106675, 305.1484228115355, 306.5978778198903, 313.3660259727644, 313.1231673026355, 304.30614078333883, 310.0778139201961, 305.86075565088146, 311.04509545916386, 309.1554965042494, 323.17569827071713, 315.6133869311824, 299.0778755675462, 314.29844711830464, 326.9437213073644, 316.9228962492937, 315.7529225573067, 332.2615378583445, 321.95589249243824, 322.1463830621629, 321.7275927641821, 317.36550281895444, 325.0325244245556, 331.5115060780851, 325.3509172568007, 309.36534218891654, 300.51233731327704, 298.982228662457, 304.0275537711359, 303.63485151418155, 308.1590108017428, 313.59031336712354, 324.5110960301337, 330.59297482156506, 327.55427439466354, 323.6864043378532, 327.50320652233705, 328.9687833715245, 339.38338677176137, 344.05273653476286, 338.0920228742981, 339.9627987342025, 353.27517532796935, 356.2544626399019, 357.9318274014981, 362.0689227732137, 364.14478459711347, 360.57616570806175, 362.2889024951751, 359.4660681299002, 363.65983892474907, 359.9505085677166, 354.107311978634, 351.9590609526303, 355.3026720316803, 351.1041787905059, 355.6626813784693, 351.208006294204, 345.34283258909073, 349.88696869457556, 343.1312344740311, 340.38904402519285, 337.7226631803288, 333.8501099758611, 333.5997223933791, 332.10408363798217, 336.02014429088, 346.06154626943913, 346.85172013342105, 344.81974713963945, 339.86296327450714, 331.30124879135013, 329.7468937657707, 329.2000635002758, 333.52630100144194, 335.05218382852354, 340.4967818157371, 339.4469167384719, 339.03392298977343, 348.73876903535563, 350.2267211165732, 351.6159537770023, 348.28146248201705, 348.40336099388577, 349.9479492276253, 357.89468390800266, 359.71398188453503, 355.667199588334, 364.538132324733, 367.7916351557313, 372.5361472492402, 386.68010297313634, 393.47600578288916, 396.5221658609917, 391.1393774594288, 392.6094096197138, 398.2891590788789, 398.1132480336191, 404.23755683253626, 402.9911576989693, 401.8426328995272, 403.4901876944152, 403.3624158016454, 403.43636557787573, 407.3396124148418, 405.41493274618165, 400.9486115704279, 397.20308329067416, 405.24313570161627, 393.38977398234397, 393.5667993806361, 391.5956856604048, 385.56511210123455, 397.73290443062933, 403.05921090912955, 395.61269198758333, 400.6732376725912, 417.4180400636582, 446.5642547111032, 456.53008032873936, 449.26364321684025, 446.3808681728655, 461.9112025447132, 455.04412266688183, 505.76637420681686, 510.37727765166903, 504.57173611838124, 489.4051506835562, 495.11487744153106, 503.531830358037, 498.1943929562418, 494.93952292226106, 498.5030874873014, 495.0883413380134, 487.74040520532174, 485.95202371956884, 495.63461779218125, 493.02427547180906, 505.94151148917047, 499.1998408485772, 499.82384064963793, 489.89817221407054, 497.6467283045897, 503.12913642807865, 504.83139000632696, 513.527110699186, 512.1491462854764, 506.16980500259353, 503.37743491166253, 518.0802508230407, 520.8519801649439, 524.5066248924346, 528.2524763718748, 553.3312626876294, 531.3409560896519, 519.7001613099877, 525.1440204997099, 519.5906224829255, 517.733086007549, 524.4204717018132, 519.3073721027205, 529.8060361420639, 524.9892162634723, 525.2254614107908, 518.4325454432112, 521.1499960355761, 531.4687659570806, 523.7624688507028, 516.5214527188419, 513.3061066756671, 521.6387758073688, 524.6947096356403, 530.5188209125957, 518.6396203143279, 507.376496559835, 507.23696802328107, 513.1970023975546, 503.9252432209055, 507.3771311369687, 498.7897731924756, 496.6034113533152, 500.8245403498184, 500.9288787957246, 492.2795068218518, 496.9438551489888, 495.8298726736964, 492.5449997672332, 490.6445969764646, 494.99089036468115, 502.7828719638386, 513.9907401513658, 521.3665072725379, 522.3266905900982, 527.4759612148323, 522.2143885017143, 514.0156226022374, 508.86261598564994, 511.6910440261702, 524.3468691817508, 542.7033791603551, 536.4442001873724, 524.351853841482, 536.3289241029778, 530.8270832232214, 522.5107922527243, 529.9173827329066, 517.7072030391025, 522.2198841589267, 543.8006208717944, 544.6253851467833, 533.2290989625869, 513.4596301185491, 522.0857519045406, 517.0650272570585, 515.1346511552989, 499.64197652180326], \"mode\": \"line\", \"name\": \"LASSO each timestep\"}], {\"title\": \"LASSO each timestep\", \"autosize\": false, \"width\": 900, \"height\": 600, \"yaxis\": {\"type\": \"log\", \"autorange\": true}}, {\"showLink\": true, \"linkText\": \"Export to plot.ly\"})});</script>"
],
"text/plain": [
"<IPython.core.display.HTML object>"
]
},
"metadata": {}
}
]
},
{
"cell_type": "code",
"metadata": {
"id": "BYPqcJmNfkZi",
"outputId": "fefa1de6-b7d2-4e80-ac47-99a7e265bfdc",
"colab": {
"resources": {
"http://localhost:8080/static/components/requirejs/require.js": {
"data": "LyoqIHZpbTogZXQ6dHM9NDpzdz00OnN0cz00CiAqIEBsaWNlbnNlIFJlcXVpcmVKUyAyLjEuMjIgQ29weXJpZ2h0IChjKSAyMDEwLTIwMTUsIFRoZSBEb2pvIEZvdW5kYXRpb24gQWxsIFJpZ2h0cyBSZXNlcnZlZC4KICogQXZhaWxhYmxlIHZpYSB0aGUgTUlUIG9yIG5ldyBCU0QgbGljZW5zZS4KICogc2VlOiBodHRwOi8vZ2l0aHViLmNvbS9qcmJ1cmtlL3JlcXVpcmVqcyBmb3IgZGV0YWlscwogKi8KLy9Ob3QgdXNpbmcgc3RyaWN0OiB1bmV2ZW4gc3RyaWN0IHN1cHBvcnQgaW4gYnJvd3NlcnMsICMzOTIsIGFuZCBjYXVzZXMKLy9wcm9ibGVtcyB3aXRoIHJlcXVpcmVqcy5leGVjKCkvdHJhbnNwaWxlciBwbHVnaW5zIHRoYXQgbWF5IG5vdCBiZSBzdHJpY3QuCi8qanNsaW50IHJlZ2V4cDogdHJ1ZSwgbm9tZW46IHRydWUsIHNsb3BweTogdHJ1ZSAqLwovKmdsb2JhbCB3aW5kb3csIG5hdmlnYXRvciwgZG9jdW1lbnQsIGltcG9ydFNjcmlwdHMsIHNldFRpbWVvdXQsIG9wZXJhICovCgp2YXIgcmVxdWlyZWpzLCByZXF1aXJlLCBkZWZpbmU7CihmdW5jdGlvbiAoZ2xvYmFsKSB7CiAgICB2YXIgcmVxLCBzLCBoZWFkLCBiYXNlRWxlbWVudCwgZGF0YU1haW4sIHNyYywKICAgICAgICBpbnRlcmFjdGl2ZVNjcmlwdCwgY3VycmVudGx5QWRkaW5nU2NyaXB0LCBtYWluU2NyaXB0LCBzdWJQYXRoLAogICAgICAgIHZlcnNpb24gPSAnMi4xLjIyJywKICAgICAgICBjb21tZW50UmVnRXhwID0gLyhcL1wqKFtcc1xTXSo/KVwqXC98KFteOl18XilcL1wvKC4qKSQpL21nLAogICAgICAgIGNqc1JlcXVpcmVSZWdFeHAgPSAvW14uXVxzKnJlcXVpcmVccypcKFxzKlsiJ10oW14nIlxzXSspWyInXVxzKlwpL2csCiAgICAgICAganNTdWZmaXhSZWdFeHAgPSAvXC5qcyQvLAogICAgICAgIGN1cnJEaXJSZWdFeHAgPSAvXlwuXC8vLAogICAgICAgIG9wID0gT2JqZWN0LnByb3RvdHlwZSwKICAgICAgICBvc3RyaW5nID0gb3AudG9TdHJpbmcsCiAgICAgICAgaGFzT3duID0gb3AuaGFzT3duUHJvcGVydHksCiAgICAgICAgYXAgPSBBcnJheS5wcm90b3R5cGUsCiAgICAgICAgaXNCcm93c2VyID0gISEodHlwZW9mIHdpbmRvdyAhPT0gJ3VuZGVmaW5lZCcgJiYgdHlwZW9mIG5hdmlnYXRvciAhPT0gJ3VuZGVmaW5lZCcgJiYgd2luZG93LmRvY3VtZW50KSwKICAgICAgICBpc1dlYldvcmtlciA9ICFpc0Jyb3dzZXIgJiYgdHlwZW9mIGltcG9ydFNjcmlwdHMgIT09ICd1bmRlZmluZWQnLAogICAgICAgIC8vUFMzIGluZGljYXRlcyBsb2FkZWQgYW5kIGNvbXBsZXRlLCBidXQgbmVlZCB0byB3YWl0IGZvciBjb21wbGV0ZQogICAgICAgIC8vc3BlY2lmaWNhbGx5LiBTZXF1ZW5jZSBpcyAnbG9hZGluZycsICdsb2FkZWQnLCBleGVjdXRpb24sCiAgICAgICAgLy8gdGhlbiAnY29tcGxldGUnLiBUaGUgVUEgY2hlY2sgaXMgdW5mb3J0dW5hdGUsIGJ1dCBub3Qgc3VyZSBob3cKICAgICAgICAvL3RvIGZlYXR1cmUgdGVzdCB3L28gY2F1c2luZyBwZXJmIGlzc3Vlcy4KICAgICAgICByZWFkeVJlZ0V4cCA9IGlzQnJvd3NlciAmJiBuYXZpZ2F0b3IucGxhdGZvcm0gPT09ICdQTEFZU1RBVElPTiAzJyA/CiAgICAgICAgICAgICAgICAgICAgICAvXmNvbXBsZXRlJC8gOiAvXihjb21wbGV0ZXxsb2FkZWQpJC8sCiAgICAgICAgZGVmQ29udGV4dE5hbWUgPSAnXycsCiAgICAgICAgLy9PaCB0aGUgdHJhZ2VkeSwgZGV0ZWN0aW5nIG9wZXJhLiBTZWUgdGhlIHVzYWdlIG9mIGlzT3BlcmEgZm9yIHJlYXNvbi4KICAgICAgICBpc09wZXJhID0gdHlwZW9mIG9wZXJhICE9PSAndW5kZWZpbmVkJyAmJiBvcGVyYS50b1N0cmluZygpID09PSAnW29iamVjdCBPcGVyYV0nLAogICAgICAgIGNvbnRleHRzID0ge30sCiAgICAgICAgY2ZnID0ge30sCiAgICAgICAgZ2xvYmFsRGVmUXVldWUgPSBbXSwKICAgICAgICB1c2VJbnRlcmFjdGl2ZSA9IGZhbHNlOwoKICAgIGZ1bmN0aW9uIGlzRnVuY3Rpb24oaXQpIHsKICAgICAgICByZXR1cm4gb3N0cmluZy5jYWxsKGl0KSA9PT0gJ1tvYmplY3QgRnVuY3Rpb25dJzsKICAgIH0KCiAgICBmdW5jdGlvbiBpc0FycmF5KGl0KSB7CiAgICAgICAgcmV0dXJuIG9zdHJpbmcuY2FsbChpdCkgPT09ICdbb2JqZWN0IEFycmF5XSc7CiAgICB9CgogICAgLyoqCiAgICAgKiBIZWxwZXIgZnVuY3Rpb24gZm9yIGl0ZXJhdGluZyBvdmVyIGFuIGFycmF5LiBJZiB0aGUgZnVuYyByZXR1cm5zCiAgICAgKiBhIHRydWUgdmFsdWUsIGl0IHdpbGwgYnJlYWsgb3V0IG9mIHRoZSBsb29wLgogICAgICovCiAgICBmdW5jdGlvbiBlYWNoKGFyeSwgZnVuYykgewogICAgICAgIGlmIChhcnkpIHsKICAgICAgICAgICAgdmFyIGk7CiAgICAgICAgICAgIGZvciAoaSA9IDA7IGkgPCBhcnkubGVuZ3RoOyBpICs9IDEpIHsKICAgICAgICAgICAgICAgIGlmIChhcnlbaV0gJiYgZnVuYyhhcnlbaV0sIGksIGFyeSkpIHsKICAgICAgICAgICAgICAgICAgICBicmVhazsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgIH0KICAgIH0KCiAgICAvKioKICAgICAqIEhlbHBlciBmdW5jdGlvbiBmb3IgaXRlcmF0aW5nIG92ZXIgYW4gYXJyYXkgYmFja3dhcmRzLiBJZiB0aGUgZnVuYwogICAgICogcmV0dXJucyBhIHRydWUgdmFsdWUsIGl0IHdpbGwgYnJlYWsgb3V0IG9mIHRoZSBsb29wLgogICAgICovCiAgICBmdW5jdGlvbiBlYWNoUmV2ZXJzZShhcnksIGZ1bmMpIHsKICAgICAgICBpZiAoYXJ5KSB7CiAgICAgICAgICAgIHZhciBpOwogICAgICAgICAgICBmb3IgKGkgPSBhcnkubGVuZ3RoIC0gMTsgaSA+IC0xOyBpIC09IDEpIHsKICAgICAgICAgICAgICAgIGlmIChhcnlbaV0gJiYgZnVuYyhhcnlbaV0sIGksIGFyeSkpIHsKICAgICAgICAgICAgICAgICAgICBicmVhazsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgIH0KICAgIH0KCiAgICBmdW5jdGlvbiBoYXNQcm9wKG9iaiwgcHJvcCkgewogICAgICAgIHJldHVybiBoYXNPd24uY2FsbChvYmosIHByb3ApOwogICAgfQoKICAgIGZ1bmN0aW9uIGdldE93bihvYmosIHByb3ApIHsKICAgICAgICByZXR1cm4gaGFzUHJvcChvYmosIHByb3ApICYmIG9ialtwcm9wXTsKICAgIH0KCiAgICAvKioKICAgICAqIEN5Y2xlcyBvdmVyIHByb3BlcnRpZXMgaW4gYW4gb2JqZWN0IGFuZCBjYWxscyBhIGZ1bmN0aW9uIGZvciBlYWNoCiAgICAgKiBwcm9wZXJ0eSB2YWx1ZS4gSWYgdGhlIGZ1bmN0aW9uIHJldHVybnMgYSB0cnV0aHkgdmFsdWUsIHRoZW4gdGhlCiAgICAgKiBpdGVyYXRpb24gaXMgc3RvcHBlZC4KICAgICAqLwogICAgZnVuY3Rpb24gZWFjaFByb3Aob2JqLCBmdW5jKSB7CiAgICAgICAgdmFyIHByb3A7CiAgICAgICAgZm9yIChwcm9wIGluIG9iaikgewogICAgICAgICAgICBpZiAoaGFzUHJvcChvYmosIHByb3ApKSB7CiAgICAgICAgICAgICAgICBpZiAoZnVuYyhvYmpbcHJvcF0sIHByb3ApKSB7CiAgICAgICAgICAgICAgICAgICAgYnJlYWs7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICB9CgogICAgLyoqCiAgICAgKiBTaW1wbGUgZnVuY3Rpb24gdG8gbWl4IGluIHByb3BlcnRpZXMgZnJvbSBzb3VyY2UgaW50byB0YXJnZXQsCiAgICAgKiBidXQgb25seSBpZiB0YXJnZXQgZG9lcyBub3QgYWxyZWFkeSBoYXZlIGEgcHJvcGVydHkgb2YgdGhlIHNhbWUgbmFtZS4KICAgICAqLwogICAgZnVuY3Rpb24gbWl4aW4odGFyZ2V0LCBzb3VyY2UsIGZvcmNlLCBkZWVwU3RyaW5nTWl4aW4pIHsKICAgICAgICBpZiAoc291cmNlKSB7CiAgICAgICAgICAgIGVhY2hQcm9wKHNvdXJjZSwgZnVuY3Rpb24gKHZhbHVlLCBwcm9wKSB7CiAgICAgICAgICAgICAgICBpZiAoZm9yY2UgfHwgIWhhc1Byb3AodGFyZ2V0LCBwcm9wKSkgewogICAgICAgICAgICAgICAgICAgIGlmIChkZWVwU3RyaW5nTWl4aW4gJiYgdHlwZW9mIHZhbHVlID09PSAnb2JqZWN0JyAmJiB2YWx1ZSAmJgogICAgICAgICAgICAgICAgICAgICAgICAhaXNBcnJheSh2YWx1ZSkgJiYgIWlzRnVuY3Rpb24odmFsdWUpICYmCiAgICAgICAgICAgICAgICAgICAgICAgICEodmFsdWUgaW5zdGFuY2VvZiBSZWdFeHApKSB7CgogICAgICAgICAgICAgICAgICAgICAgICBpZiAoIXRhcmdldFtwcm9wXSkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgdGFyZ2V0W3Byb3BdID0ge307CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgbWl4aW4odGFyZ2V0W3Byb3BdLCB2YWx1ZSwgZm9yY2UsIGRlZXBTdHJpbmdNaXhpbik7CiAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgICAgICAgICAgICAgdGFyZ2V0W3Byb3BdID0gdmFsdWU7CiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9KTsKICAgICAgICB9CiAgICAgICAgcmV0dXJuIHRhcmdldDsKICAgIH0KCiAgICAvL1NpbWlsYXIgdG8gRnVuY3Rpb24ucHJvdG90eXBlLmJpbmQsIGJ1dCB0aGUgJ3RoaXMnIG9iamVjdCBpcyBzcGVjaWZpZWQKICAgIC8vZmlyc3QsIHNpbmNlIGl0IGlzIGVhc2llciB0byByZWFkL2ZpZ3VyZSBvdXQgd2hhdCAndGhpcycgd2lsbCBiZS4KICAgIGZ1bmN0aW9uIGJpbmQob2JqLCBmbikgewogICAgICAgIHJldHVybiBmdW5jdGlvbiAoKSB7CiAgICAgICAgICAgIHJldHVybiBmbi5hcHBseShvYmosIGFyZ3VtZW50cyk7CiAgICAgICAgfTsKICAgIH0KCiAgICBmdW5jdGlvbiBzY3JpcHRzKCkgewogICAgICAgIHJldHVybiBkb2N1bWVudC5nZXRFbGVtZW50c0J5VGFnTmFtZSgnc2NyaXB0Jyk7CiAgICB9CgogICAgZnVuY3Rpb24gZGVmYXVsdE9uRXJyb3IoZXJyKSB7CiAgICAgICAgdGhyb3cgZXJyOwogICAgfQoKICAgIC8vQWxsb3cgZ2V0dGluZyBhIGdsb2JhbCB0aGF0IGlzIGV4cHJlc3NlZCBpbgogICAgLy9kb3Qgbm90YXRpb24sIGxpa2UgJ2EuYi5jJy4KICAgIGZ1bmN0aW9uIGdldEdsb2JhbCh2YWx1ZSkgewogICAgICAgIGlmICghdmFsdWUpIHsKICAgICAgICAgICAgcmV0dXJuIHZhbHVlOwogICAgICAgIH0KICAgICAgICB2YXIgZyA9IGdsb2JhbDsKICAgICAgICBlYWNoKHZhbHVlLnNwbGl0KCcuJyksIGZ1bmN0aW9uIChwYXJ0KSB7CiAgICAgICAgICAgIGcgPSBnW3BhcnRdOwogICAgICAgIH0pOwogICAgICAgIHJldHVybiBnOwogICAgfQoKICAgIC8qKgogICAgICogQ29uc3RydWN0cyBhbiBlcnJvciB3aXRoIGEgcG9pbnRlciB0byBhbiBVUkwgd2l0aCBtb3JlIGluZm9ybWF0aW9uLgogICAgICogQHBhcmFtIHtTdHJpbmd9IGlkIHRoZSBlcnJvciBJRCB0aGF0IG1hcHMgdG8gYW4gSUQgb24gYSB3ZWIgcGFnZS4KICAgICAqIEBwYXJhbSB7U3RyaW5nfSBtZXNzYWdlIGh1bWFuIHJlYWRhYmxlIGVycm9yLgogICAgICogQHBhcmFtIHtFcnJvcn0gW2Vycl0gdGhlIG9yaWdpbmFsIGVycm9yLCBpZiB0aGVyZSBpcyBvbmUuCiAgICAgKgogICAgICogQHJldHVybnMge0Vycm9yfQogICAgICovCiAgICBmdW5jdGlvbiBtYWtlRXJyb3IoaWQsIG1zZywgZXJyLCByZXF1aXJlTW9kdWxlcykgewogICAgICAgIHZhciBlID0gbmV3IEVycm9yKG1zZyArICdcbmh0dHA6Ly9yZXF1aXJlanMub3JnL2RvY3MvZXJyb3JzLmh0bWwjJyArIGlkKTsKICAgICAgICBlLnJlcXVpcmVUeXBlID0gaWQ7CiAgICAgICAgZS5yZXF1aXJlTW9kdWxlcyA9IHJlcXVpcmVNb2R1bGVzOwogICAgICAgIGlmIChlcnIpIHsKICAgICAgICAgICAgZS5vcmlnaW5hbEVycm9yID0gZXJyOwogICAgICAgIH0KICAgICAgICByZXR1cm4gZTsKICAgIH0KCiAgICBpZiAodHlwZW9mIGRlZmluZSAhPT0gJ3VuZGVmaW5lZCcpIHsKICAgICAgICAvL0lmIGEgZGVmaW5lIGlzIGFscmVhZHkgaW4gcGxheSB2aWEgYW5vdGhlciBBTUQgbG9hZGVyLAogICAgICAgIC8vZG8gbm90IG92ZXJ3cml0ZS4KICAgICAgICByZXR1cm47CiAgICB9CgogICAgaWYgKHR5cGVvZiByZXF1aXJlanMgIT09ICd1bmRlZmluZWQnKSB7CiAgICAgICAgaWYgKGlzRnVuY3Rpb24ocmVxdWlyZWpzKSkgewogICAgICAgICAgICAvL0RvIG5vdCBvdmVyd3JpdGUgYW4gZXhpc3RpbmcgcmVxdWlyZWpzIGluc3RhbmNlLgogICAgICAgICAgICByZXR1cm47CiAgICAgICAgfQogICAgICAgIGNmZyA9IHJlcXVpcmVqczsKICAgICAgICByZXF1aXJlanMgPSB1bmRlZmluZWQ7CiAgICB9CgogICAgLy9BbGxvdyBmb3IgYSByZXF1aXJlIGNvbmZpZyBvYmplY3QKICAgIGlmICh0eXBlb2YgcmVxdWlyZSAhPT0gJ3VuZGVmaW5lZCcgJiYgIWlzRnVuY3Rpb24ocmVxdWlyZSkpIHsKICAgICAgICAvL2Fzc3VtZSBpdCBpcyBhIGNvbmZpZyBvYmplY3QuCiAgICAgICAgY2ZnID0gcmVxdWlyZTsKICAgICAgICByZXF1aXJlID0gdW5kZWZpbmVkOwogICAgfQoKICAgIGZ1bmN0aW9uIG5ld0NvbnRleHQoY29udGV4dE5hbWUpIHsKICAgICAgICB2YXIgaW5DaGVja0xvYWRlZCwgTW9kdWxlLCBjb250ZXh0LCBoYW5kbGVycywKICAgICAgICAgICAgY2hlY2tMb2FkZWRUaW1lb3V0SWQsCiAgICAgICAgICAgIGNvbmZpZyA9IHsKICAgICAgICAgICAgICAgIC8vRGVmYXVsdHMuIERvIG5vdCBzZXQgYSBkZWZhdWx0IGZvciBtYXAKICAgICAgICAgICAgICAgIC8vY29uZmlnIHRvIHNwZWVkIHVwIG5vcm1hbGl6ZSgpLCB3aGljaAogICAgICAgICAgICAgICAgLy93aWxsIHJ1biBmYXN0ZXIgaWYgdGhlcmUgaXMgbm8gZGVmYXVsdC4KICAgICAgICAgICAgICAgIHdhaXRTZWNvbmRzOiA3LAogICAgICAgICAgICAgICAgYmFzZVVybDogJy4vJywKICAgICAgICAgICAgICAgIHBhdGhzOiB7fSwKICAgICAgICAgICAgICAgIGJ1bmRsZXM6IHt9LAogICAgICAgICAgICAgICAgcGtnczoge30sCiAgICAgICAgICAgICAgICBzaGltOiB7fSwKICAgICAgICAgICAgICAgIGNvbmZpZzoge30KICAgICAgICAgICAgfSwKICAgICAgICAgICAgcmVnaXN0cnkgPSB7fSwKICAgICAgICAgICAgLy9yZWdpc3RyeSBvZiBqdXN0IGVuYWJsZWQgbW9kdWxlcywgdG8gc3BlZWQKICAgICAgICAgICAgLy9jeWNsZSBicmVha2luZyBjb2RlIHdoZW4gbG90cyBvZiBtb2R1bGVzCiAgICAgICAgICAgIC8vYXJlIHJlZ2lzdGVyZWQsIGJ1dCBub3QgYWN0aXZhdGVkLgogICAgICAgICAgICBlbmFibGVkUmVnaXN0cnkgPSB7fSwKICAgICAgICAgICAgdW5kZWZFdmVudHMgPSB7fSwKICAgICAgICAgICAgZGVmUXVldWUgPSBbXSwKICAgICAgICAgICAgZGVmaW5lZCA9IHt9LAogICAgICAgICAgICB1cmxGZXRjaGVkID0ge30sCiAgICAgICAgICAgIGJ1bmRsZXNNYXAgPSB7fSwKICAgICAgICAgICAgcmVxdWlyZUNvdW50ZXIgPSAxLAogICAgICAgICAgICB1bm5vcm1hbGl6ZWRDb3VudGVyID0gMTsKCiAgICAgICAgLyoqCiAgICAgICAgICogVHJpbXMgdGhlIC4gYW5kIC4uIGZyb20gYW4gYXJyYXkgb2YgcGF0aCBzZWdtZW50cy4KICAgICAgICAgKiBJdCB3aWxsIGtlZXAgYSBsZWFkaW5nIHBhdGggc2VnbWVudCBpZiBhIC4uIHdpbGwgYmVjb21lCiAgICAgICAgICogdGhlIGZpcnN0IHBhdGggc2VnbWVudCwgdG8gaGVscCB3aXRoIG1vZHVsZSBuYW1lIGxvb2t1cHMsCiAgICAgICAgICogd2hpY2ggYWN0IGxpa2UgcGF0aHMsIGJ1dCBjYW4gYmUgcmVtYXBwZWQuIEJ1dCB0aGUgZW5kIHJlc3VsdCwKICAgICAgICAgKiBhbGwgcGF0aHMgdGhhdCB1c2UgdGhpcyBmdW5jdGlvbiBzaG91bGQgbG9vayBub3JtYWxpemVkLgogICAgICAgICAqIE5PVEU6IHRoaXMgbWV0aG9kIE1PRElGSUVTIHRoZSBpbnB1dCBhcnJheS4KICAgICAgICAgKiBAcGFyYW0ge0FycmF5fSBhcnkgdGhlIGFycmF5IG9mIHBhdGggc2VnbWVudHMuCiAgICAgICAgICovCiAgICAgICAgZnVuY3Rpb24gdHJpbURvdHMoYXJ5KSB7CiAgICAgICAgICAgIHZhciBpLCBwYXJ0OwogICAgICAgICAgICBmb3IgKGkgPSAwOyBpIDwgYXJ5Lmxlbmd0aDsgaSsrKSB7CiAgICAgICAgICAgICAgICBwYXJ0ID0gYXJ5W2ldOwogICAgICAgICAgICAgICAgaWYgKHBhcnQgPT09ICcuJykgewogICAgICAgICAgICAgICAgICAgIGFyeS5zcGxpY2UoaSwgMSk7CiAgICAgICAgICAgICAgICAgICAgaSAtPSAxOwogICAgICAgICAgICAgICAgfSBlbHNlIGlmIChwYXJ0ID09PSAnLi4nKSB7CiAgICAgICAgICAgICAgICAgICAgLy8gSWYgYXQgdGhlIHN0YXJ0LCBvciBwcmV2aW91cyB2YWx1ZSBpcyBzdGlsbCAuLiwKICAgICAgICAgICAgICAgICAgICAvLyBrZWVwIHRoZW0gc28gdGhhdCB3aGVuIGNvbnZlcnRlZCB0byBhIHBhdGggaXQgbWF5CiAgICAgICAgICAgICAgICAgICAgLy8gc3RpbGwgd29yayB3aGVuIGNvbnZlcnRlZCB0byBhIHBhdGgsIGV2ZW4gdGhvdWdoCiAgICAgICAgICAgICAgICAgICAgLy8gYXMgYW4gSUQgaXQgaXMgbGVzcyB0aGFuIGlkZWFsLiBJbiBsYXJnZXIgcG9pbnQKICAgICAgICAgICAgICAgICAgICAvLyByZWxlYXNlcywgbWF5IGJlIGJldHRlciB0byBqdXN0IGtpY2sgb3V0IGFuIGVycm9yLgogICAgICAgICAgICAgICAgICAgIGlmIChpID09PSAwIHx8IChpID09PSAxICYmIGFyeVsyXSA9PT0gJy4uJykgfHwgYXJ5W2kgLSAxXSA9PT0gJy4uJykgewogICAgICAgICAgICAgICAgICAgICAgICBjb250aW51ZTsKICAgICAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKGkgPiAwKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIGFyeS5zcGxpY2UoaSAtIDEsIDIpOwogICAgICAgICAgICAgICAgICAgICAgICBpIC09IDI7CiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CiAgICAgICAgfQoKICAgICAgICAvKioKICAgICAgICAgKiBHaXZlbiBhIHJlbGF0aXZlIG1vZHVsZSBuYW1lLCBsaWtlIC4vc29tZXRoaW5nLCBub3JtYWxpemUgaXQgdG8KICAgICAgICAgKiBhIHJlYWwgbmFtZSB0aGF0IGNhbiBiZSBtYXBwZWQgdG8gYSBwYXRoLgogICAgICAgICAqIEBwYXJhbSB7U3RyaW5nfSBuYW1lIHRoZSByZWxhdGl2ZSBuYW1lCiAgICAgICAgICogQHBhcmFtIHtTdHJpbmd9IGJhc2VOYW1lIGEgcmVhbCBuYW1lIHRoYXQgdGhlIG5hbWUgYXJnIGlzIHJlbGF0aXZlCiAgICAgICAgICogdG8uCiAgICAgICAgICogQHBhcmFtIHtCb29sZWFufSBhcHBseU1hcCBhcHBseSB0aGUgbWFwIGNvbmZpZyB0byB0aGUgdmFsdWUuIFNob3VsZAogICAgICAgICAqIG9ubHkgYmUgZG9uZSBpZiB0aGlzIG5vcm1hbGl6YXRpb24gaXMgZm9yIGEgZGVwZW5kZW5jeSBJRC4KICAgICAgICAgKiBAcmV0dXJucyB7U3RyaW5nfSBub3JtYWxpemVkIG5hbWUKICAgICAgICAgKi8KICAgICAgICBmdW5jdGlvbiBub3JtYWxpemUobmFtZSwgYmFzZU5hbWUsIGFwcGx5TWFwKSB7CiAgICAgICAgICAgIHZhciBwa2dNYWluLCBtYXBWYWx1ZSwgbmFtZVBhcnRzLCBpLCBqLCBuYW1lU2VnbWVudCwgbGFzdEluZGV4LAogICAgICAgICAgICAgICAgZm91bmRNYXAsIGZvdW5kSSwgZm91bmRTdGFyTWFwLCBzdGFySSwgbm9ybWFsaXplZEJhc2VQYXJ0cywKICAgICAgICAgICAgICAgIGJhc2VQYXJ0cyA9IChiYXNlTmFtZSAmJiBiYXNlTmFtZS5zcGxpdCgnLycpKSwKICAgICAgICAgICAgICAgIG1hcCA9IGNvbmZpZy5tYXAsCiAgICAgICAgICAgICAgICBzdGFyTWFwID0gbWFwICYmIG1hcFsnKiddOwoKICAgICAgICAgICAgLy9BZGp1c3QgYW55IHJlbGF0aXZlIHBhdGhzLgogICAgICAgICAgICBpZiAobmFtZSkgewogICAgICAgICAgICAgICAgbmFtZSA9IG5hbWUuc3BsaXQoJy8nKTsKICAgICAgICAgICAgICAgIGxhc3RJbmRleCA9IG5hbWUubGVuZ3RoIC0gMTsKCiAgICAgICAgICAgICAgICAvLyBJZiB3YW50aW5nIG5vZGUgSUQgY29tcGF0aWJpbGl0eSwgc3RyaXAgLmpzIGZyb20gZW5kCiAgICAgICAgICAgICAgICAvLyBvZiBJRHMuIEhhdmUgdG8gZG8gdGhpcyBoZXJlLCBhbmQgbm90IGluIG5hbWVUb1VybAogICAgICAgICAgICAgICAgLy8gYmVjYXVzZSBub2RlIGFsbG93cyBlaXRoZXIgLmpzIG9yIG5vbiAuanMgdG8gbWFwCiAgICAgICAgICAgICAgICAvLyB0byBzYW1lIGZpbGUuCiAgICAgICAgICAgICAgICBpZiAoY29uZmlnLm5vZGVJZENvbXBhdCAmJiBqc1N1ZmZpeFJlZ0V4cC50ZXN0KG5hbWVbbGFzdEluZGV4XSkpIHsKICAgICAgICAgICAgICAgICAgICBuYW1lW2xhc3RJbmRleF0gPSBuYW1lW2xhc3RJbmRleF0ucmVwbGFjZShqc1N1ZmZpeFJlZ0V4cCwgJycpOwogICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgIC8vIFN0YXJ0cyB3aXRoIGEgJy4nIHNvIG5lZWQgdGhlIGJhc2VOYW1lCiAgICAgICAgICAgICAgICBpZiAobmFtZVswXS5jaGFyQXQoMCkgPT09ICcuJyAmJiBiYXNlUGFydHMpIHsKICAgICAgICAgICAgICAgICAgICAvL0NvbnZlcnQgYmFzZU5hbWUgdG8gYXJyYXksIGFuZCBsb3Agb2ZmIHRoZSBsYXN0IHBhcnQsCiAgICAgICAgICAgICAgICAgICAgLy9zbyB0aGF0IC4gbWF0Y2hlcyB0aGF0ICdkaXJlY3RvcnknIGFuZCBub3QgbmFtZSBvZiB0aGUgYmFzZU5hbWUncwogICAgICAgICAgICAgICAgICAgIC8vbW9kdWxlLiBGb3IgaW5zdGFuY2UsIGJhc2VOYW1lIG9mICdvbmUvdHdvL3RocmVlJywgbWFwcyB0bwogICAgICAgICAgICAgICAgICAgIC8vJ29uZS90d28vdGhyZWUuanMnLCBidXQgd2Ugd2FudCB0aGUgZGlyZWN0b3J5LCAnb25lL3R3bycgZm9yCiAgICAgICAgICAgICAgICAgICAgLy90aGlzIG5vcm1hbGl6YXRpb24uCiAgICAgICAgICAgICAgICAgICAgbm9ybWFsaXplZEJhc2VQYXJ0cyA9IGJhc2VQYXJ0cy5zbGljZSgwLCBiYXNlUGFydHMubGVuZ3RoIC0gMSk7CiAgICAgICAgICAgICAgICAgICAgbmFtZSA9IG5vcm1hbGl6ZWRCYXNlUGFydHMuY29uY2F0KG5hbWUpOwogICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgIHRyaW1Eb3RzKG5hbWUpOwogICAgICAgICAgICAgICAgbmFtZSA9IG5hbWUuam9pbignLycpOwogICAgICAgICAgICB9CgogICAgICAgICAgICAvL0FwcGx5IG1hcCBjb25maWcgaWYgYXZhaWxhYmxlLgogICAgICAgICAgICBpZiAoYXBwbHlNYXAgJiYgbWFwICYmIChiYXNlUGFydHMgfHwgc3Rhck1hcCkpIHsKICAgICAgICAgICAgICAgIG5hbWVQYXJ0cyA9IG5hbWUuc3BsaXQoJy8nKTsKCiAgICAgICAgICAgICAgICBvdXRlckxvb3A6IGZvciAoaSA9IG5hbWVQYXJ0cy5sZW5ndGg7IGkgPiAwOyBpIC09IDEpIHsKICAgICAgICAgICAgICAgICAgICBuYW1lU2VnbWVudCA9IG5hbWVQYXJ0cy5zbGljZSgwLCBpKS5qb2luKCcvJyk7CgogICAgICAgICAgICAgICAgICAgIGlmIChiYXNlUGFydHMpIHsKICAgICAgICAgICAgICAgICAgICAgICAgLy9GaW5kIHRoZSBsb25nZXN0IGJhc2VOYW1lIHNlZ21lbnQgbWF0Y2ggaW4gdGhlIGNvbmZpZy4KICAgICAgICAgICAgICAgICAgICAgICAgLy9TbywgZG8gam9pbnMgb24gdGhlIGJpZ2dlc3QgdG8gc21hbGxlc3QgbGVuZ3RocyBvZiBiYXNlUGFydHMuCiAgICAgICAgICAgICAgICAgICAgICAgIGZvciAoaiA9IGJhc2VQYXJ0cy5sZW5ndGg7IGogPiAwOyBqIC09IDEpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1hcFZhbHVlID0gZ2V0T3duKG1hcCwgYmFzZVBhcnRzLnNsaWNlKDAsIGopLmpvaW4oJy8nKSk7CgogICAgICAgICAgICAgICAgICAgICAgICAgICAgLy9iYXNlTmFtZSBzZWdtZW50IGhhcyBjb25maWcsIGZpbmQgaWYgaXQgaGFzIG9uZSBmb3IKICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vdGhpcyBuYW1lLgogICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKG1hcFZhbHVlKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWFwVmFsdWUgPSBnZXRPd24obWFwVmFsdWUsIG5hbWVTZWdtZW50KTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAobWFwVmFsdWUpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy9NYXRjaCwgdXBkYXRlIG5hbWUgdG8gdGhlIG5ldyB2YWx1ZS4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZm91bmRNYXAgPSBtYXBWYWx1ZTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZm91bmRJID0gaTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWsgb3V0ZXJMb29wOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICAgICAgLy9DaGVjayBmb3IgYSBzdGFyIG1hcCBtYXRjaCwgYnV0IGp1c3QgaG9sZCBvbiB0byBpdCwKICAgICAgICAgICAgICAgICAgICAvL2lmIHRoZXJlIGlzIGEgc2hvcnRlciBzZWdtZW50IG1hdGNoIGxhdGVyIGluIGEgbWF0Y2hpbmcKICAgICAgICAgICAgICAgICAgICAvL2NvbmZpZywgdGhlbiBmYXZvciBvdmVyIHRoaXMgc3RhciBtYXAuCiAgICAgICAgICAgICAgICAgICAgaWYgKCFmb3VuZFN0YXJNYXAgJiYgc3Rhck1hcCAmJiBnZXRPd24oc3Rhck1hcCwgbmFtZVNlZ21lbnQpKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIGZvdW5kU3Rhck1hcCA9IGdldE93bihzdGFyTWFwLCBuYW1lU2VnbWVudCk7CiAgICAgICAgICAgICAgICAgICAgICAgIHN0YXJJID0gaTsKICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgaWYgKCFmb3VuZE1hcCAmJiBmb3VuZFN0YXJNYXApIHsKICAgICAgICAgICAgICAgICAgICBmb3VuZE1hcCA9IGZvdW5kU3Rhck1hcDsKICAgICAgICAgICAgICAgICAgICBmb3VuZEkgPSBzdGFySTsKICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICBpZiAoZm91bmRNYXApIHsKICAgICAgICAgICAgICAgICAgICBuYW1lUGFydHMuc3BsaWNlKDAsIGZvdW5kSSwgZm91bmRNYXApOwogICAgICAgICAgICAgICAgICAgIG5hbWUgPSBuYW1lUGFydHMuam9pbignLycpOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CgogICAgICAgICAgICAvLyBJZiB0aGUgbmFtZSBwb2ludHMgdG8gYSBwYWNrYWdlJ3MgbmFtZSwgdXNlCiAgICAgICAgICAgIC8vIHRoZSBwYWNrYWdlIG1haW4gaW5zdGVhZC4KICAgICAgICAgICAgcGtnTWFpbiA9IGdldE93bihjb25maWcucGtncywgbmFtZSk7CgogICAgICAgICAgICByZXR1cm4gcGtnTWFpbiA/IHBrZ01haW4gOiBuYW1lOwogICAgICAgIH0KCiAgICAgICAgZnVuY3Rpb24gcmVtb3ZlU2NyaXB0KG5hbWUpIHsKICAgICAgICAgICAgaWYgKGlzQnJvd3NlcikgewogICAgICAgICAgICAgICAgZWFjaChzY3JpcHRzKCksIGZ1bmN0aW9uIChzY3JpcHROb2RlKSB7CiAgICAgICAgICAgICAgICAgICAgaWYgKHNjcmlwdE5vZGUuZ2V0QXR0cmlidXRlKCdkYXRhLXJlcXVpcmVtb2R1bGUnKSA9PT0gbmFtZSAmJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgc2NyaXB0Tm9kZS5nZXRBdHRyaWJ1dGUoJ2RhdGEtcmVxdWlyZWNvbnRleHQnKSA9PT0gY29udGV4dC5jb250ZXh0TmFtZSkgewogICAgICAgICAgICAgICAgICAgICAgICBzY3JpcHROb2RlLnBhcmVudE5vZGUucmVtb3ZlQ2hpbGQoc2NyaXB0Tm9kZSk7CiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiB0cnVlOwogICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIH0pOwogICAgICAgICAgICB9CiAgICAgICAgfQoKICAgICAgICBmdW5jdGlvbiBoYXNQYXRoRmFsbGJhY2soaWQpIHsKICAgICAgICAgICAgdmFyIHBhdGhDb25maWcgPSBnZXRPd24oY29uZmlnLnBhdGhzLCBpZCk7CiAgICAgICAgICAgIGlmIChwYXRoQ29uZmlnICYmIGlzQXJyYXkocGF0aENvbmZpZykgJiYgcGF0aENvbmZpZy5sZW5ndGggPiAxKSB7CiAgICAgICAgICAgICAgICAvL1BvcCBvZmYgdGhlIGZpcnN0IGFycmF5IHZhbHVlLCBzaW5jZSBpdCBmYWlsZWQsIGFuZAogICAgICAgICAgICAgICAgLy9yZXRyeQogICAgICAgICAgICAgICAgcGF0aENvbmZpZy5zaGlmdCgpOwogICAgICAgICAgICAgICAgY29udGV4dC5yZXF1aXJlLnVuZGVmKGlkKTsKCiAgICAgICAgICAgICAgICAvL0N1c3RvbSByZXF1aXJlIHRoYXQgZG9lcyBub3QgZG8gbWFwIHRyYW5zbGF0aW9uLCBzaW5jZQogICAgICAgICAgICAgICAgLy9JRCBpcyAiYWJzb2x1dGUiLCBhbHJlYWR5IG1hcHBlZC9yZXNvbHZlZC4KICAgICAgICAgICAgICAgIGNvbnRleHQubWFrZVJlcXVpcmUobnVsbCwgewogICAgICAgICAgICAgICAgICAgIHNraXBNYXA6IHRydWUKICAgICAgICAgICAgICAgIH0pKFtpZF0pOwoKICAgICAgICAgICAgICAgIHJldHVybiB0cnVlOwogICAgICAgICAgICB9CiAgICAgICAgfQoKICAgICAgICAvL1R1cm5zIGEgcGx1Z2luIXJlc291cmNlIHRvIFtwbHVnaW4sIHJlc291cmNlXQogICAgICAgIC8vd2l0aCB0aGUgcGx1Z2luIGJlaW5nIHVuZGVmaW5lZCBpZiB0aGUgbmFtZQogICAgICAgIC8vZGlkIG5vdCBoYXZlIGEgcGx1Z2luIHByZWZpeC4KICAgICAgICBmdW5jdGlvbiBzcGxpdFByZWZpeChuYW1lKSB7CiAgICAgICAgICAgIHZhciBwcmVmaXgsCiAgICAgICAgICAgICAgICBpbmRleCA9IG5hbWUgPyBuYW1lLmluZGV4T2YoJyEnKSA6IC0xOwogICAgICAgICAgICBpZiAoaW5kZXggPiAtMSkgewogICAgICAgICAgICAgICAgcHJlZml4ID0gbmFtZS5zdWJzdHJpbmcoMCwgaW5kZXgpOwogICAgICAgICAgICAgICAgbmFtZSA9IG5hbWUuc3Vic3RyaW5nKGluZGV4ICsgMSwgbmFtZS5sZW5ndGgpOwogICAgICAgICAgICB9CiAgICAgICAgICAgIHJldHVybiBbcHJlZml4LCBuYW1lXTsKICAgICAgICB9CgogICAgICAgIC8qKgogICAgICAgICAqIENyZWF0ZXMgYSBtb2R1bGUgbWFwcGluZyB0aGF0IGluY2x1ZGVzIHBsdWdpbiBwcmVmaXgsIG1vZHVsZQogICAgICAgICAqIG5hbWUsIGFuZCBwYXRoLiBJZiBwYXJlbnRNb2R1bGVNYXAgaXMgcHJvdmlkZWQgaXQgd2lsbAogICAgICAgICAqIGFsc28gbm9ybWFsaXplIHRoZSBuYW1lIHZpYSByZXF1aXJlLm5vcm1hbGl6ZSgpCiAgICAgICAgICoKICAgICAgICAgKiBAcGFyYW0ge1N0cmluZ30gbmFtZSB0aGUgbW9kdWxlIG5hbWUKICAgICAgICAgKiBAcGFyYW0ge1N0cmluZ30gW3BhcmVudE1vZHVsZU1hcF0gcGFyZW50IG1vZHVsZSBtYXAKICAgICAgICAgKiBmb3IgdGhlIG1vZHVsZSBuYW1lLCB1c2VkIHRvIHJlc29sdmUgcmVsYXRpdmUgbmFtZXMuCiAgICAgICAgICogQHBhcmFtIHtCb29sZWFufSBpc05vcm1hbGl6ZWQ6IGlzIHRoZSBJRCBhbHJlYWR5IG5vcm1hbGl6ZWQuCiAgICAgICAgICogVGhpcyBpcyB0cnVlIGlmIHRoaXMgY2FsbCBpcyBkb25lIGZvciBhIGRlZmluZSgpIG1vZHVsZSBJRC4KICAgICAgICAgKiBAcGFyYW0ge0Jvb2xlYW59IGFwcGx5TWFwOiBhcHBseSB0aGUgbWFwIGNvbmZpZyB0byB0aGUgSUQuCiAgICAgICAgICogU2hvdWxkIG9ubHkgYmUgdHJ1ZSBpZiB0aGlzIG1hcCBpcyBmb3IgYSBkZXBlbmRlbmN5LgogICAgICAgICAqCiAgICAgICAgICogQHJldHVybnMge09iamVjdH0KICAgICAgICAgKi8KICAgICAgICBmdW5jdGlvbiBtYWtlTW9kdWxlTWFwKG5hbWUsIHBhcmVudE1vZHVsZU1hcCwgaXNOb3JtYWxpemVkLCBhcHBseU1hcCkgewogICAgICAgICAgICB2YXIgdXJsLCBwbHVnaW5Nb2R1bGUsIHN1ZmZpeCwgbmFtZVBhcnRzLAogICAgICAgICAgICAgICAgcHJlZml4ID0gbnVsbCwKICAgICAgICAgICAgICAgIHBhcmVudE5hbWUgPSBwYXJlbnRNb2R1bGVNYXAgPyBwYXJlbnRNb2R1bGVNYXAubmFtZSA6IG51bGwsCiAgICAgICAgICAgICAgICBvcmlnaW5hbE5hbWUgPSBuYW1lLAogICAgICAgICAgICAgICAgaXNEZWZpbmUgPSB0cnVlLAogICAgICAgICAgICAgICAgbm9ybWFsaXplZE5hbWUgPSAnJzsKCiAgICAgICAgICAgIC8vSWYgbm8gbmFtZSwgdGhlbiBpdCBtZWFucyBpdCBpcyBhIHJlcXVpcmUgY2FsbCwgZ2VuZXJhdGUgYW4KICAgICAgICAgICAgLy9pbnRlcm5hbCBuYW1lLgogICAgICAgICAgICBpZiAoIW5hbWUpIHsKICAgICAgICAgICAgICAgIGlzRGVmaW5lID0gZmFsc2U7CiAgICAgICAgICAgICAgICBuYW1lID0gJ19AcicgKyAocmVxdWlyZUNvdW50ZXIgKz0gMSk7CiAgICAgICAgICAgIH0KCiAgICAgICAgICAgIG5hbWVQYXJ0cyA9IHNwbGl0UHJlZml4KG5hbWUpOwogICAgICAgICAgICBwcmVmaXggPSBuYW1lUGFydHNbMF07CiAgICAgICAgICAgIG5hbWUgPSBuYW1lUGFydHNbMV07CgogICAgICAgICAgICBpZiAocHJlZml4KSB7CiAgICAgICAgICAgICAgICBwcmVmaXggPSBub3JtYWxpemUocHJlZml4LCBwYXJlbnROYW1lLCBhcHBseU1hcCk7CiAgICAgICAgICAgICAgICBwbHVnaW5Nb2R1bGUgPSBnZXRPd24oZGVmaW5lZCwgcHJlZml4KTsKICAgICAgICAgICAgfQoKICAgICAgICAgICAgLy9BY2NvdW50IGZvciByZWxhdGl2ZSBwYXRocyBpZiB0aGVyZSBpcyBhIGJhc2UgbmFtZS4KICAgICAgICAgICAgaWYgKG5hbWUpIHsKICAgICAgICAgICAgICAgIGlmIChwcmVmaXgpIHsKICAgICAgICAgICAgICAgICAgICBpZiAocGx1Z2luTW9kdWxlICYmIHBsdWdpbk1vZHVsZS5ub3JtYWxpemUpIHsKICAgICAgICAgICAgICAgICAgICAgICAgLy9QbHVnaW4gaXMgbG9hZGVkLCB1c2UgaXRzIG5vcm1hbGl6ZSBtZXRob2QuCiAgICAgICAgICAgICAgICAgICAgICAgIG5vcm1hbGl6ZWROYW1lID0gcGx1Z2luTW9kdWxlLm5vcm1hbGl6ZShuYW1lLCBmdW5jdGlvbiAobmFtZSkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIG5vcm1hbGl6ZShuYW1lLCBwYXJlbnROYW1lLCBhcHBseU1hcCk7CiAgICAgICAgICAgICAgICAgICAgICAgIH0pOwogICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgICAgICAgICAgICAgIC8vIElmIG5lc3RlZCBwbHVnaW4gcmVmZXJlbmNlcywgdGhlbiBkbyBub3QgdHJ5IHRvCiAgICAgICAgICAgICAgICAgICAgICAgIC8vIG5vcm1hbGl6ZSwgYXMgaXQgd2lsbCBub3Qgbm9ybWFsaXplIGNvcnJlY3RseS4gVGhpcwogICAgICAgICAgICAgICAgICAgICAgICAvLyBwbGFjZXMgYSByZXN0cmljdGlvbiBvbiByZXNvdXJjZUlkcywgYW5kIHRoZSBsb25nZXIKICAgICAgICAgICAgICAgICAgICAgICAgLy8gdGVybSBzb2x1dGlvbiBpcyBub3QgdG8gbm9ybWFsaXplIHVudGlsIHBsdWdpbnMgYXJlCiAgICAgICAgICAgICAgICAgICAgICAgIC8vIGxvYWRlZCBhbmQgYWxsIG5vcm1hbGl6YXRpb25zIHRvIGFsbG93IGZvciBhc3luYwogICAgICAgICAgICAgICAgICAgICAgICAvLyBsb2FkaW5nIG9mIGEgbG9hZGVyIHBsdWdpbi4gQnV0IGZvciBub3csIGZpeGVzIHRoZQogICAgICAgICAgICAgICAgICAgICAgICAvLyBjb21tb24gdXNlcy4gRGV0YWlscyBpbiAjMTEzMQogICAgICAgICAgICAgICAgICAgICAgICBub3JtYWxpemVkTmFtZSA9IG5hbWUuaW5kZXhPZignIScpID09PSAtMSA/CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm9ybWFsaXplKG5hbWUsIHBhcmVudE5hbWUsIGFwcGx5TWFwKSA6CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmFtZTsKICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICAgICAgICAgIC8vQSByZWd1bGFyIG1vZHVsZS4KICAgICAgICAgICAgICAgICAgICBub3JtYWxpemVkTmFtZSA9IG5vcm1hbGl6ZShuYW1lLCBwYXJlbnROYW1lLCBhcHBseU1hcCk7CgogICAgICAgICAgICAgICAgICAgIC8vTm9ybWFsaXplZCBuYW1lIG1heSBiZSBhIHBsdWdpbiBJRCBkdWUgdG8gbWFwIGNvbmZpZwogICAgICAgICAgICAgICAgICAgIC8vYXBwbGljYXRpb24gaW4gbm9ybWFsaXplLiBUaGUgbWFwIGNvbmZpZyB2YWx1ZXMgbXVzdAogICAgICAgICAgICAgICAgICAgIC8vYWxyZWFkeSBiZSBub3JtYWxpemVkLCBzbyBkbyBub3QgbmVlZCB0byByZWRvIHRoYXQgcGFydC4KICAgICAgICAgICAgICAgICAgICBuYW1lUGFydHMgPSBzcGxpdFByZWZpeChub3JtYWxpemVkTmFtZSk7CiAgICAgICAgICAgICAgICAgICAgcHJlZml4ID0gbmFtZVBhcnRzWzBdOwogICAgICAgICAgICAgICAgICAgIG5vcm1hbGl6ZWROYW1lID0gbmFtZVBhcnRzWzFdOwogICAgICAgICAgICAgICAgICAgIGlzTm9ybWFsaXplZCA9IHRydWU7CgogICAgICAgICAgICAgICAgICAgIHVybCA9IGNvbnRleHQubmFtZVRvVXJsKG5vcm1hbGl6ZWROYW1lKTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQoKICAgICAgICAgICAgLy9JZiB0aGUgaWQgaXMgYSBwbHVnaW4gaWQgdGhhdCBjYW5ub3QgYmUgZGV0ZXJtaW5lZCBpZiBpdCBuZWVkcwogICAgICAgICAgICAvL25vcm1hbGl6YXRpb24sIHN0YW1wIGl0IHdpdGggYSB1bmlxdWUgSUQgc28gdHdvIG1hdGNoaW5nIHJlbGF0aXZlCiAgICAgICAgICAgIC8vaWRzIHRoYXQgbWF5IGNvbmZsaWN0IGNhbiBiZSBzZXBhcmF0ZS4KICAgICAgICAgICAgc3VmZml4ID0gcHJlZml4ICYmICFwbHVnaW5Nb2R1bGUgJiYgIWlzTm9ybWFsaXplZCA/CiAgICAgICAgICAgICAgICAgICAgICdfdW5ub3JtYWxpemVkJyArICh1bm5vcm1hbGl6ZWRDb3VudGVyICs9IDEpIDoKICAgICAgICAgICAgICAgICAgICAgJyc7CgogICAgICAgICAgICByZXR1cm4gewogICAgICAgICAgICAgICAgcHJlZml4OiBwcmVmaXgsCiAgICAgICAgICAgICAgICBuYW1lOiBub3JtYWxpemVkTmFtZSwKICAgICAgICAgICAgICAgIHBhcmVudE1hcDogcGFyZW50TW9kdWxlTWFwLAogICAgICAgICAgICAgICAgdW5ub3JtYWxpemVkOiAhIXN1ZmZpeCwKICAgICAgICAgICAgICAgIHVybDogdXJsLAogICAgICAgICAgICAgICAgb3JpZ2luYWxOYW1lOiBvcmlnaW5hbE5hbWUsCiAgICAgICAgICAgICAgICBpc0RlZmluZTogaXNEZWZpbmUsCiAgICAgICAgICAgICAgICBpZDogKHByZWZpeCA/CiAgICAgICAgICAgICAgICAgICAgICAgIHByZWZpeCArICchJyArIG5vcm1hbGl6ZWROYW1lIDoKICAgICAgICAgICAgICAgICAgICAgICAgbm9ybWFsaXplZE5hbWUpICsgc3VmZml4CiAgICAgICAgICAgIH07CiAgICAgICAgfQoKICAgICAgICBmdW5jdGlvbiBnZXRNb2R1bGUoZGVwTWFwKSB7CiAgICAgICAgICAgIHZhciBpZCA9IGRlcE1hcC5pZCwKICAgICAgICAgICAgICAgIG1vZCA9IGdldE93bihyZWdpc3RyeSwgaWQpOwoKICAgICAgICAgICAgaWYgKCFtb2QpIHsKICAgICAgICAgICAgICAgIG1vZCA9IHJlZ2lzdHJ5W2lkXSA9IG5ldyBjb250ZXh0Lk1vZHVsZShkZXBNYXApOwogICAgICAgICAgICB9CgogICAgICAgICAgICByZXR1cm4gbW9kOwogICAgICAgIH0KCiAgICAgICAgZnVuY3Rpb24gb24oZGVwTWFwLCBuYW1lLCBmbikgewogICAgICAgICAgICB2YXIgaWQgPSBkZXBNYXAuaWQsCiAgICAgICAgICAgICAgICBtb2QgPSBnZXRPd24ocmVnaXN0cnksIGlkKTsKCiAgICAgICAgICAgIGlmIChoYXNQcm9wKGRlZmluZWQsIGlkKSAmJgogICAgICAgICAgICAgICAgICAgICghbW9kIHx8IG1vZC5kZWZpbmVFbWl0Q29tcGxldGUpKSB7CiAgICAgICAgICAgICAgICBpZiAobmFtZSA9PT0gJ2RlZmluZWQnKSB7CiAgICAgICAgICAgICAgICAgICAgZm4oZGVmaW5lZFtpZF0pOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICAgICAgbW9kID0gZ2V0TW9kdWxlKGRlcE1hcCk7CiAgICAgICAgICAgICAgICBpZiAobW9kLmVycm9yICYmIG5hbWUgPT09ICdlcnJvcicpIHsKICAgICAgICAgICAgICAgICAgICBmbihtb2QuZXJyb3IpOwogICAgICAgICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgICAgICAgICBtb2Qub24obmFtZSwgZm4pOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CiAgICAgICAgfQoKICAgICAgICBmdW5jdGlvbiBvbkVycm9yKGVyciwgZXJyYmFjaykgewogICAgICAgICAgICB2YXIgaWRzID0gZXJyLnJlcXVpcmVNb2R1bGVzLAogICAgICAgICAgICAgICAgbm90aWZpZWQgPSBmYWxzZTsKCiAgICAgICAgICAgIGlmIChlcnJiYWNrKSB7CiAgICAgICAgICAgICAgICBlcnJiYWNrKGVycik7CiAgICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgICAgICBlYWNoKGlkcywgZnVuY3Rpb24gKGlkKSB7CiAgICAgICAgICAgICAgICAgICAgdmFyIG1vZCA9IGdldE93bihyZWdpc3RyeSwgaWQpOwogICAgICAgICAgICAgICAgICAgIGlmIChtb2QpIHsKICAgICAgICAgICAgICAgICAgICAgICAgLy9TZXQgZXJyb3Igb24gbW9kdWxlLCBzbyBpdCBza2lwcyB0aW1lb3V0IGNoZWNrcy4KICAgICAgICAgICAgICAgICAgICAgICAgbW9kLmVycm9yID0gZXJyOwogICAgICAgICAgICAgICAgICAgICAgICBpZiAobW9kLmV2ZW50cy5lcnJvcikgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgbm90aWZpZWQgPSB0cnVlOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9kLmVtaXQoJ2Vycm9yJywgZXJyKTsKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIH0pOwoKICAgICAgICAgICAgICAgIGlmICghbm90aWZpZWQpIHsKICAgICAgICAgICAgICAgICAgICByZXEub25FcnJvcihlcnIpOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CiAgICAgICAgfQoKICAgICAgICAvKioKICAgICAgICAgKiBJbnRlcm5hbCBtZXRob2QgdG8gdHJhbnNmZXIgZ2xvYmFsUXVldWUgaXRlbXMgdG8gdGhpcyBjb250ZXh0J3MKICAgICAgICAgKiBkZWZRdWV1ZS4KICAgICAgICAgKi8KICAgICAgICBmdW5jdGlvbiB0YWtlR2xvYmFsUXVldWUoKSB7CiAgICAgICAgICAgIC8vUHVzaCBhbGwgdGhlIGdsb2JhbERlZlF1ZXVlIGl0ZW1zIGludG8gdGhlIGNvbnRleHQncyBkZWZRdWV1ZQogICAgICAgICAgICBpZiAoZ2xvYmFsRGVmUXVldWUubGVuZ3RoKSB7CiAgICAgICAgICAgICAgICBlYWNoKGdsb2JhbERlZlF1ZXVlLCBmdW5jdGlvbihxdWV1ZUl0ZW0pIHsKICAgICAgICAgICAgICAgICAgICB2YXIgaWQgPSBxdWV1ZUl0ZW1bMF07CiAgICAgICAgICAgICAgICAgICAgaWYgKHR5cGVvZiBpZCA9PT0gJ3N0cmluZycpIHsKICAgICAgICAgICAgICAgICAgICAgICAgY29udGV4dC5kZWZRdWV1ZU1hcFtpZF0gPSB0cnVlOwogICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICBkZWZRdWV1ZS5wdXNoKHF1ZXVlSXRlbSk7CiAgICAgICAgICAgICAgICB9KTsKICAgICAgICAgICAgICAgIGdsb2JhbERlZlF1ZXVlID0gW107CiAgICAgICAgICAgIH0KICAgICAgICB9CgogICAgICAgIGhhbmRsZXJzID0gewogICAgICAgICAgICAncmVxdWlyZSc6IGZ1bmN0aW9uIChtb2QpIHsKICAgICAgICAgICAgICAgIGlmIChtb2QucmVxdWlyZSkgewogICAgICAgICAgICAgICAgICAgIHJldHVybiBtb2QucmVxdWlyZTsKICAgICAgICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIChtb2QucmVxdWlyZSA9IGNvbnRleHQubWFrZVJlcXVpcmUobW9kLm1hcCkpOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAnZXhwb3J0cyc6IGZ1bmN0aW9uIChtb2QpIHsKICAgICAgICAgICAgICAgIG1vZC51c2luZ0V4cG9ydHMgPSB0cnVlOwogICAgICAgICAgICAgICAgaWYgKG1vZC5tYXAuaXNEZWZpbmUpIHsKICAgICAgICAgICAgICAgICAgICBpZiAobW9kLmV4cG9ydHMpIHsKICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIChkZWZpbmVkW21vZC5tYXAuaWRdID0gbW9kLmV4cG9ydHMpOwogICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiAobW9kLmV4cG9ydHMgPSBkZWZpbmVkW21vZC5tYXAuaWRdID0ge30pOwogICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgJ21vZHVsZSc6IGZ1bmN0aW9uIChtb2QpIHsKICAgICAgICAgICAgICAgIGlmIChtb2QubW9kdWxlKSB7CiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIG1vZC5tb2R1bGU7CiAgICAgICAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICAgICAgICAgIHJldHVybiAobW9kLm1vZHVsZSA9IHsKICAgICAgICAgICAgICAgICAgICAgICAgaWQ6IG1vZC5tYXAuaWQsCiAgICAgICAgICAgICAgICAgICAgICAgIHVyaTogbW9kLm1hcC51cmwsCiAgICAgICAgICAgICAgICAgICAgICAgIGNvbmZpZzogZnVuY3Rpb24gKCkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGdldE93bihjb25maWcuY29uZmlnLCBtb2QubWFwLmlkKSB8fCB7fTsKICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgZXhwb3J0czogbW9kLmV4cG9ydHMgfHwgKG1vZC5leHBvcnRzID0ge30pCiAgICAgICAgICAgICAgICAgICAgfSk7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICB9OwoKICAgICAgICBmdW5jdGlvbiBjbGVhblJlZ2lzdHJ5KGlkKSB7CiAgICAgICAgICAgIC8vQ2xlYW4gdXAgbWFjaGluZXJ5IHVzZWQgZm9yIHdhaXRpbmcgbW9kdWxlcy4KICAgICAgICAgICAgZGVsZXRlIHJlZ2lzdHJ5W2lkXTsKICAgICAgICAgICAgZGVsZXRlIGVuYWJsZWRSZWdpc3RyeVtpZF07CiAgICAgICAgfQoKICAgICAgICBmdW5jdGlvbiBicmVha0N5Y2xlKG1vZCwgdHJhY2VkLCBwcm9jZXNzZWQpIHsKICAgICAgICAgICAgdmFyIGlkID0gbW9kLm1hcC5pZDsKCiAgICAgICAgICAgIGlmIChtb2QuZXJyb3IpIHsKICAgICAgICAgICAgICAgIG1vZC5lbWl0KCdlcnJvcicsIG1vZC5lcnJvcik7CiAgICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgICAgICB0cmFjZWRbaWRdID0gdHJ1ZTsKICAgICAgICAgICAgICAgIGVhY2gobW9kLmRlcE1hcHMsIGZ1bmN0aW9uIChkZXBNYXAsIGkpIHsKICAgICAgICAgICAgICAgICAgICB2YXIgZGVwSWQgPSBkZXBNYXAuaWQsCiAgICAgICAgICAgICAgICAgICAgICAgIGRlcCA9IGdldE93bihyZWdpc3RyeSwgZGVwSWQpOwoKICAgICAgICAgICAgICAgICAgICAvL09ubHkgZm9yY2UgdGhpbmdzIHRoYXQgaGF2ZSBub3QgY29tcGxldGVkCiAgICAgICAgICAgICAgICAgICAgLy9iZWluZyBkZWZpbmVkLCBzbyBzdGlsbCBpbiB0aGUgcmVnaXN0cnksCiAgICAgICAgICAgICAgICAgICAgLy9hbmQgb25seSBpZiBpdCBoYXMgbm90IGJlZW4gbWF0Y2hlZCB1cAogICAgICAgICAgICAgICAgICAgIC8vaW4gdGhlIG1vZHVsZSBhbHJlYWR5LgogICAgICAgICAgICAgICAgICAgIGlmIChkZXAgJiYgIW1vZC5kZXBNYXRjaGVkW2ldICYmICFwcm9jZXNzZWRbZGVwSWRdKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChnZXRPd24odHJhY2VkLCBkZXBJZCkpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1vZC5kZWZpbmVEZXAoaSwgZGVmaW5lZFtkZXBJZF0pOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9kLmNoZWNrKCk7IC8vcGFzcyBmYWxzZT8KICAgICAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrQ3ljbGUoZGVwLCB0cmFjZWQsIHByb2Nlc3NlZCk7CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICB9KTsKICAgICAgICAgICAgICAgIHByb2Nlc3NlZFtpZF0gPSB0cnVlOwogICAgICAgICAgICB9CiAgICAgICAgfQoKICAgICAgICBmdW5jdGlvbiBjaGVja0xvYWRlZCgpIHsKICAgICAgICAgICAgdmFyIGVyciwgdXNpbmdQYXRoRmFsbGJhY2ssCiAgICAgICAgICAgICAgICB3YWl0SW50ZXJ2YWwgPSBjb25maWcud2FpdFNlY29uZHMgKiAxMDAwLAogICAgICAgICAgICAgICAgLy9JdCBpcyBwb3NzaWJsZSB0byBkaXNhYmxlIHRoZSB3YWl0IGludGVydmFsIGJ5IHVzaW5nIHdhaXRTZWNvbmRzIG9mIDAuCiAgICAgICAgICAgICAgICBleHBpcmVkID0gd2FpdEludGVydmFsICYmIChjb250ZXh0LnN0YXJ0VGltZSArIHdhaXRJbnRlcnZhbCkgPCBuZXcgRGF0ZSgpLmdldFRpbWUoKSwKICAgICAgICAgICAgICAgIG5vTG9hZHMgPSBbXSwKICAgICAgICAgICAgICAgIHJlcUNhbGxzID0gW10sCiAgICAgICAgICAgICAgICBzdGlsbExvYWRpbmcgPSBmYWxzZSwKICAgICAgICAgICAgICAgIG5lZWRDeWNsZUNoZWNrID0gdHJ1ZTsKCiAgICAgICAgICAgIC8vRG8gbm90IGJvdGhlciBpZiB0aGlzIGNhbGwgd2FzIGEgcmVzdWx0IG9mIGEgY3ljbGUgYnJlYWsuCiAgICAgICAgICAgIGlmIChpbkNoZWNrTG9hZGVkKSB7CiAgICAgICAgICAgICAgICByZXR1cm47CiAgICAgICAgICAgIH0KCiAgICAgICAgICAgIGluQ2hlY2tMb2FkZWQgPSB0cnVlOwoKICAgICAgICAgICAgLy9GaWd1cmUgb3V0IHRoZSBzdGF0ZSBvZiBhbGwgdGhlIG1vZHVsZXMuCiAgICAgICAgICAgIGVhY2hQcm9wKGVuYWJsZWRSZWdpc3RyeSwgZnVuY3Rpb24gKG1vZCkgewogICAgICAgICAgICAgICAgdmFyIG1hcCA9IG1vZC5tYXAsCiAgICAgICAgICAgICAgICAgICAgbW9kSWQgPSBtYXAuaWQ7CgogICAgICAgICAgICAgICAgLy9Ta2lwIHRoaW5ncyB0aGF0IGFyZSBub3QgZW5hYmxlZCBvciBpbiBlcnJvciBzdGF0ZS4KICAgICAgICAgICAgICAgIGlmICghbW9kLmVuYWJsZWQpIHsKICAgICAgICAgICAgICAgICAgICByZXR1cm47CiAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgaWYgKCFtYXAuaXNEZWZpbmUpIHsKICAgICAgICAgICAgICAgICAgICByZXFDYWxscy5wdXNoKG1vZCk7CiAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgaWYgKCFtb2QuZXJyb3IpIHsKICAgICAgICAgICAgICAgICAgICAvL0lmIHRoZSBtb2R1bGUgc2hvdWxkIGJlIGV4ZWN1dGVkLCBhbmQgaXQgaGFzIG5vdAogICAgICAgICAgICAgICAgICAgIC8vYmVlbiBpbml0ZWQgYW5kIHRpbWUgaXMgdXAsIHJlbWVtYmVyIGl0LgogICAgICAgICAgICAgICAgICAgIGlmICghbW9kLmluaXRlZCAmJiBleHBpcmVkKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChoYXNQYXRoRmFsbGJhY2sobW9kSWQpKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB1c2luZ1BhdGhGYWxsYmFjayA9IHRydWU7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdGlsbExvYWRpbmcgPSB0cnVlOwogICAgICAgICAgICAgICAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgbm9Mb2Fkcy5wdXNoKG1vZElkKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlbW92ZVNjcmlwdChtb2RJZCk7CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKCFtb2QuaW5pdGVkICYmIG1vZC5mZXRjaGVkICYmIG1hcC5pc0RlZmluZSkgewogICAgICAgICAgICAgICAgICAgICAgICBzdGlsbExvYWRpbmcgPSB0cnVlOwogICAgICAgICAgICAgICAgICAgICAgICBpZiAoIW1hcC5wcmVmaXgpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vTm8gcmVhc29uIHRvIGtlZXAgbG9va2luZyBmb3IgdW5maW5pc2hlZAogICAgICAgICAgICAgICAgICAgICAgICAgICAgLy9sb2FkaW5nLiBJZiB0aGUgb25seSBzdGlsbExvYWRpbmcgaXMgYQogICAgICAgICAgICAgICAgICAgICAgICAgICAgLy9wbHVnaW4gcmVzb3VyY2UgdGhvdWdoLCBrZWVwIGdvaW5nLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgLy9iZWNhdXNlIGl0IG1heSBiZSB0aGF0IGEgcGx1Z2luIHJlc291cmNlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvL2lzIHdhaXRpbmcgb24gYSBub24tcGx1Z2luIGN5Y2xlLgogICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIChuZWVkQ3ljbGVDaGVjayA9IGZhbHNlKTsKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSk7CgogICAgICAgICAgICBpZiAoZXhwaXJlZCAmJiBub0xvYWRzLmxlbmd0aCkgewogICAgICAgICAgICAgICAgLy9JZiB3YWl0IHRpbWUgZXhwaXJlZCwgdGhyb3cgZXJyb3Igb2YgdW5sb2FkZWQgbW9kdWxlcy4KICAgICAgICAgICAgICAgIGVyciA9IG1ha2VFcnJvcigndGltZW91dCcsICdMb2FkIHRpbWVvdXQgZm9yIG1vZHVsZXM6ICcgKyBub0xvYWRzLCBudWxsLCBub0xvYWRzKTsKICAgICAgICAgICAgICAgIGVyci5jb250ZXh0TmFtZSA9IGNvbnRleHQuY29udGV4dE5hbWU7CiAgICAgICAgICAgICAgICByZXR1cm4gb25FcnJvcihlcnIpOwogICAgICAgICAgICB9CgogICAgICAgICAgICAvL05vdCBleHBpcmVkLCBjaGVjayBmb3IgYSBjeWNsZS4KICAgICAgICAgICAgaWYgKG5lZWRDeWNsZUNoZWNrKSB7CiAgICAgICAgICAgICAgICBlYWNoKHJlcUNhbGxzLCBmdW5jdGlvbiAobW9kKSB7CiAgICAgICAgICAgICAgICAgICAgYnJlYWtDeWNsZShtb2QsIHt9LCB7fSk7CiAgICAgICAgICAgICAgICB9KTsKICAgICAgICAgICAgfQoKICAgICAgICAgICAgLy9JZiBzdGlsbCB3YWl0aW5nIG9uIGxvYWRzLCBhbmQgdGhlIHdhaXRpbmcgbG9hZCBpcyBzb21ldGhpbmcKICAgICAgICAgICAgLy9vdGhlciB0aGFuIGEgcGx1Z2luIHJlc291cmNlLCBvciB0aGVyZSBhcmUgc3RpbGwgb3V0c3RhbmRpbmcKICAgICAgICAgICAgLy9zY3JpcHRzLCB0aGVuIGp1c3QgdHJ5IGJhY2sgbGF0ZXIuCiAgICAgICAgICAgIGlmICgoIWV4cGlyZWQgfHwgdXNpbmdQYXRoRmFsbGJhY2spICYmIHN0aWxsTG9hZGluZykgewogICAgICAgICAgICAgICAgLy9Tb21ldGhpbmcgaXMgc3RpbGwgd2FpdGluZyB0byBsb2FkLiBXYWl0IGZvciBpdCwgYnV0IG9ubHkKICAgICAgICAgICAgICAgIC8vaWYgYSB0aW1lb3V0IGlzIG5vdCBhbHJlYWR5IGluIGVmZmVjdC4KICAgICAgICAgICAgICAgIGlmICgoaXNCcm93c2VyIHx8IGlzV2ViV29ya2VyKSAmJiAhY2hlY2tMb2FkZWRUaW1lb3V0SWQpIHsKICAgICAgICAgICAgICAgICAgICBjaGVja0xvYWRlZFRpbWVvdXRJZCA9IHNldFRpbWVvdXQoZnVuY3Rpb24gKCkgewogICAgICAgICAgICAgICAgICAgICAgICBjaGVja0xvYWRlZFRpbWVvdXRJZCA9IDA7CiAgICAgICAgICAgICAgICAgICAgICAgIGNoZWNrTG9hZGVkKCk7CiAgICAgICAgICAgICAgICAgICAgfSwgNTApOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CgogICAgICAgICAgICBpbkNoZWNrTG9hZGVkID0gZmFsc2U7CiAgICAgICAgfQoKICAgICAgICBNb2R1bGUgPSBmdW5jdGlvbiAobWFwKSB7CiAgICAgICAgICAgIHRoaXMuZXZlbnRzID0gZ2V0T3duKHVuZGVmRXZlbnRzLCBtYXAuaWQpIHx8IHt9OwogICAgICAgICAgICB0aGlzLm1hcCA9IG1hcDsKICAgICAgICAgICAgdGhpcy5zaGltID0gZ2V0T3duKGNvbmZpZy5zaGltLCBtYXAuaWQpOwogICAgICAgICAgICB0aGlzLmRlcEV4cG9ydHMgPSBbXTsKICAgICAgICAgICAgdGhpcy5kZXBNYXBzID0gW107CiAgICAgICAgICAgIHRoaXMuZGVwTWF0Y2hlZCA9IFtdOwogICAgICAgICAgICB0aGlzLnBsdWdpbk1hcHMgPSB7fTsKICAgICAgICAgICAgdGhpcy5kZXBDb3VudCA9IDA7CgogICAgICAgICAgICAvKiB0aGlzLmV4cG9ydHMgdGhpcy5mYWN0b3J5CiAgICAgICAgICAgICAgIHRoaXMuZGVwTWFwcyA9IFtdLAogICAgICAgICAgICAgICB0aGlzLmVuYWJsZWQsIHRoaXMuZmV0Y2hlZAogICAgICAgICAgICAqLwogICAgICAgIH07CgogICAgICAgIE1vZHVsZS5wcm90b3R5cGUgPSB7CiAgICAgICAgICAgIGluaXQ6IGZ1bmN0aW9uIChkZXBNYXBzLCBmYWN0b3J5LCBlcnJiYWNrLCBvcHRpb25zKSB7CiAgICAgICAgICAgICAgICBvcHRpb25zID0gb3B0aW9ucyB8fCB7fTsKCiAgICAgICAgICAgICAgICAvL0RvIG5vdCBkbyBtb3JlIGluaXRzIGlmIGFscmVhZHkgZG9uZS4gQ2FuIGhhcHBlbiBpZiB0aGVyZQogICAgICAgICAgICAgICAgLy9hcmUgbXVsdGlwbGUgZGVmaW5lIGNhbGxzIGZvciB0aGUgc2FtZSBtb2R1bGUuIFRoYXQgaXMgbm90CiAgICAgICAgICAgICAgICAvL2Egbm9ybWFsLCBjb21tb24gY2FzZSwgYnV0IGl0IGlzIGFsc28gbm90IHVuZXhwZWN0ZWQuCiAgICAgICAgICAgICAgICBpZiAodGhpcy5pbml0ZWQpIHsKICAgICAgICAgICAgICAgICAgICByZXR1cm47CiAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgdGhpcy5mYWN0b3J5ID0gZmFjdG9yeTsKCiAgICAgICAgICAgICAgICBpZiAoZXJyYmFjaykgewogICAgICAgICAgICAgICAgICAgIC8vUmVnaXN0ZXIgZm9yIGVycm9ycyBvbiB0aGlzIG1vZHVsZS4KICAgICAgICAgICAgICAgICAgICB0aGlzLm9uKCdlcnJvcicsIGVycmJhY2spOwogICAgICAgICAgICAgICAgfSBlbHNlIGlmICh0aGlzLmV2ZW50cy5lcnJvcikgewogICAgICAgICAgICAgICAgICAgIC8vSWYgbm8gZXJyYmFjayBhbHJlYWR5LCBidXQgdGhlcmUgYXJlIGVycm9yIGxpc3RlbmVycwogICAgICAgICAgICAgICAgICAgIC8vb24gdGhpcyBtb2R1bGUsIHNldCB1cCBhbiBlcnJiYWNrIHRvIHBhc3MgdG8gdGhlIGRlcHMuCiAgICAgICAgICAgICAgICAgICAgZXJyYmFjayA9IGJpbmQodGhpcywgZnVuY3Rpb24gKGVycikgewogICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmVtaXQoJ2Vycm9yJywgZXJyKTsKICAgICAgICAgICAgICAgICAgICB9KTsKICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICAvL0RvIGEgY29weSBvZiB0aGUgZGVwZW5kZW5jeSBhcnJheSwgc28gdGhhdAogICAgICAgICAgICAgICAgLy9zb3VyY2UgaW5wdXRzIGFyZSBub3QgbW9kaWZpZWQuIEZvciBleGFtcGxlCiAgICAgICAgICAgICAgICAvLyJzaGltIiBkZXBzIGFyZSBwYXNzZWQgaW4gaGVyZSBkaXJlY3RseSwgYW5kCiAgICAgICAgICAgICAgICAvL2RvaW5nIGEgZGlyZWN0IG1vZGlmaWNhdGlvbiBvZiB0aGUgZGVwTWFwcyBhcnJheQogICAgICAgICAgICAgICAgLy93b3VsZCBhZmZlY3QgdGhhdCBjb25maWcuCiAgICAgICAgICAgICAgICB0aGlzLmRlcE1hcHMgPSBkZXBNYXBzICYmIGRlcE1hcHMuc2xpY2UoMCk7CgogICAgICAgICAgICAgICAgdGhpcy5lcnJiYWNrID0gZXJyYmFjazsKCiAgICAgICAgICAgICAgICAvL0luZGljYXRlIHRoaXMgbW9kdWxlIGhhcyBiZSBpbml0aWFsaXplZAogICAgICAgICAgICAgICAgdGhpcy5pbml0ZWQgPSB0cnVlOwoKICAgICAgICAgICAgICAgIHRoaXMuaWdub3JlID0gb3B0aW9ucy5pZ25vcmU7CgogICAgICAgICAgICAgICAgLy9Db3VsZCBoYXZlIG9wdGlvbiB0byBpbml0IHRoaXMgbW9kdWxlIGluIGVuYWJsZWQgbW9kZSwKICAgICAgICAgICAgICAgIC8vb3IgY291bGQgaGF2ZSBiZWVuIHByZXZpb3VzbHkgbWFya2VkIGFzIGVuYWJsZWQuIEhvd2V2ZXIsCiAgICAgICAgICAgICAgICAvL3RoZSBkZXBlbmRlbmNpZXMgYXJlIG5vdCBrbm93biB1bnRpbCBpbml0IGlzIGNhbGxlZC4gU28KICAgICAgICAgICAgICAgIC8vaWYgZW5hYmxlZCBwcmV2aW91c2x5LCBub3cgdHJpZ2dlciBkZXBlbmRlbmNpZXMgYXMgZW5hYmxlZC4KICAgICAgICAgICAgICAgIGlmIChvcHRpb25zLmVuYWJsZWQgfHwgdGhpcy5lbmFibGVkKSB7CiAgICAgICAgICAgICAgICAgICAgLy9FbmFibGUgdGhpcyBtb2R1bGUgYW5kIGRlcGVuZGVuY2llcy4KICAgICAgICAgICAgICAgICAgICAvL1dpbGwgY2FsbCB0aGlzLmNoZWNrKCkKICAgICAgICAgICAgICAgICAgICB0aGlzLmVuYWJsZSgpOwogICAgICAgICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgICAgICAgICB0aGlzLmNoZWNrKCk7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCgogICAgICAgICAgICBkZWZpbmVEZXA6IGZ1bmN0aW9uIChpLCBkZXBFeHBvcnRzKSB7CiAgICAgICAgICAgICAgICAvL0JlY2F1c2Ugb2YgY3ljbGVzLCBkZWZpbmVkIGNhbGxiYWNrIGZvciBhIGdpdmVuCiAgICAgICAgICAgICAgICAvL2V4cG9ydCBjYW4gYmUgY2FsbGVkIG1vcmUgdGhhbiBvbmNlLgogICAgICAgICAgICAgICAgaWYgKCF0aGlzLmRlcE1hdGNoZWRbaV0pIHsKICAgICAgICAgICAgICAgICAgICB0aGlzLmRlcE1hdGNoZWRbaV0gPSB0cnVlOwogICAgICAgICAgICAgICAgICAgIHRoaXMuZGVwQ291bnQgLT0gMTsKICAgICAgICAgICAgICAgICAgICB0aGlzLmRlcEV4cG9ydHNbaV0gPSBkZXBFeHBvcnRzOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAoKICAgICAgICAgICAgZmV0Y2g6IGZ1bmN0aW9uICgpIHsKICAgICAgICAgICAgICAgIGlmICh0aGlzLmZldGNoZWQpIHsKICAgICAgICAgICAgICAgICAgICByZXR1cm47CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICB0aGlzLmZldGNoZWQgPSB0cnVlOwoKICAgICAgICAgICAgICAgIGNvbnRleHQuc3RhcnRUaW1lID0gKG5ldyBEYXRlKCkpLmdldFRpbWUoKTsKCiAgICAgICAgICAgICAgICB2YXIgbWFwID0gdGhpcy5tYXA7CgogICAgICAgICAgICAgICAgLy9JZiB0aGUgbWFuYWdlciBpcyBmb3IgYSBwbHVnaW4gbWFuYWdlZCByZXNvdXJjZSwKICAgICAgICAgICAgICAgIC8vYXNrIHRoZSBwbHVnaW4gdG8gbG9hZCBpdCBub3cuCiAgICAgICAgICAgICAgICBpZiAodGhpcy5zaGltKSB7CiAgICAgICAgICAgICAgICAgICAgY29udGV4dC5tYWtlUmVxdWlyZSh0aGlzLm1hcCwgewogICAgICAgICAgICAgICAgICAgICAgICBlbmFibGVCdWlsZENhbGxiYWNrOiB0cnVlCiAgICAgICAgICAgICAgICAgICAgfSkodGhpcy5zaGltLmRlcHMgfHwgW10sIGJpbmQodGhpcywgZnVuY3Rpb24gKCkgewogICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gbWFwLnByZWZpeCA/IHRoaXMuY2FsbFBsdWdpbigpIDogdGhpcy5sb2FkKCk7CiAgICAgICAgICAgICAgICAgICAgfSkpOwogICAgICAgICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgICAgICAgICAvL1JlZ3VsYXIgZGVwZW5kZW5jeS4KICAgICAgICAgICAgICAgICAgICByZXR1cm4gbWFwLnByZWZpeCA/IHRoaXMuY2FsbFBsdWdpbigpIDogdGhpcy5sb2FkKCk7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCgogICAgICAgICAgICBsb2FkOiBmdW5jdGlvbiAoKSB7CiAgICAgICAgICAgICAgICB2YXIgdXJsID0gdGhpcy5tYXAudXJsOwoKICAgICAgICAgICAgICAgIC8vUmVndWxhciBkZXBlbmRlbmN5LgogICAgICAgICAgICAgICAgaWYgKCF1cmxGZXRjaGVkW3VybF0pIHsKICAgICAgICAgICAgICAgICAgICB1cmxGZXRjaGVkW3VybF0gPSB0cnVlOwogICAgICAgICAgICAgICAgICAgIGNvbnRleHQubG9hZCh0aGlzLm1hcC5pZCwgdXJsKTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKCiAgICAgICAgICAgIC8qKgogICAgICAgICAgICAgKiBDaGVja3MgaWYgdGhlIG1vZHVsZSBpcyByZWFkeSB0byBkZWZpbmUgaXRzZWxmLCBhbmQgaWYgc28sCiAgICAgICAgICAgICAqIGRlZmluZSBpdC4KICAgICAgICAgICAgICovCiAgICAgICAgICAgIGNoZWNrOiBmdW5jdGlvbiAoKSB7CiAgICAgICAgICAgICAgICBpZiAoIXRoaXMuZW5hYmxlZCB8fCB0aGlzLmVuYWJsaW5nKSB7CiAgICAgICAgICAgICAgICAgICAgcmV0dXJuOwogICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgIHZhciBlcnIsIGNqc01vZHVsZSwKICAgICAgICAgICAgICAgICAgICBpZCA9IHRoaXMubWFwLmlkLAogICAgICAgICAgICAgICAgICAgIGRlcEV4cG9ydHMgPSB0aGlzLmRlcEV4cG9ydHMsCiAgICAgICAgICAgICAgICAgICAgZXhwb3J0cyA9IHRoaXMuZXhwb3J0cywKICAgICAgICAgICAgICAgICAgICBmYWN0b3J5ID0gdGhpcy5mYWN0b3J5OwoKICAgICAgICAgICAgICAgIGlmICghdGhpcy5pbml0ZWQpIHsKICAgICAgICAgICAgICAgICAgICAvLyBPbmx5IGZldGNoIGlmIG5vdCBhbHJlYWR5IGluIHRoZSBkZWZRdWV1ZS4KICAgICAgICAgICAgICAgICAgICBpZiAoIWhhc1Byb3AoY29udGV4dC5kZWZRdWV1ZU1hcCwgaWQpKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuZmV0Y2goKTsKICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKHRoaXMuZXJyb3IpIHsKICAgICAgICAgICAgICAgICAgICB0aGlzLmVtaXQoJ2Vycm9yJywgdGhpcy5lcnJvcik7CiAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKCF0aGlzLmRlZmluaW5nKSB7CiAgICAgICAgICAgICAgICAgICAgLy9UaGUgZmFjdG9yeSBjb3VsZCB0cmlnZ2VyIGFub3RoZXIgcmVxdWlyZSBjYWxsCiAgICAgICAgICAgICAgICAgICAgLy90aGF0IHdvdWxkIHJlc3VsdCBpbiBjaGVja2luZyB0aGlzIG1vZHVsZSB0bwogICAgICAgICAgICAgICAgICAgIC8vZGVmaW5lIGl0c2VsZiBhZ2Fpbi4gSWYgYWxyZWFkeSBpbiB0aGUgcHJvY2VzcwogICAgICAgICAgICAgICAgICAgIC8vb2YgZG9pbmcgdGhhdCwgc2tpcCB0aGlzIHdvcmsuCiAgICAgICAgICAgICAgICAgICAgdGhpcy5kZWZpbmluZyA9IHRydWU7CgogICAgICAgICAgICAgICAgICAgIGlmICh0aGlzLmRlcENvdW50IDwgMSAmJiAhdGhpcy5kZWZpbmVkKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChpc0Z1bmN0aW9uKGZhY3RvcnkpKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0cnkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4cG9ydHMgPSBjb250ZXh0LmV4ZWNDYihpZCwgZmFjdG9yeSwgZGVwRXhwb3J0cywgZXhwb3J0cyk7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IGNhdGNoIChlKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZXJyID0gZTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBGYXZvciByZXR1cm4gdmFsdWUgb3ZlciBleHBvcnRzLiBJZiBub2RlL2NqcyBpbiBwbGF5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gdGhlbiB3aWxsIG5vdCBoYXZlIGEgcmV0dXJuIHZhbHVlIGFueXdheS4gRmF2b3IKICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIG1vZHVsZS5leHBvcnRzIGFzc2lnbm1lbnQgb3ZlciBleHBvcnRzIG9iamVjdC4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmICh0aGlzLm1hcC5pc0RlZmluZSAmJiBleHBvcnRzID09PSB1bmRlZmluZWQpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjanNNb2R1bGUgPSB0aGlzLm1vZHVsZTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAoY2pzTW9kdWxlKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4cG9ydHMgPSBjanNNb2R1bGUuZXhwb3J0czsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKHRoaXMudXNpbmdFeHBvcnRzKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vZXhwb3J0cyBhbHJlYWR5IHNldCB0aGUgZGVmaW5lZCB2YWx1ZS4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZXhwb3J0cyA9IHRoaXMuZXhwb3J0czsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGVycikgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIElmIHRoZXJlIGlzIGFuIGVycm9yIGxpc3RlbmVyLCBmYXZvciBwYXNzaW5nCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gdG8gdGhhdCBpbnN0ZWFkIG9mIHRocm93aW5nIGFuIGVycm9yLiBIb3dldmVyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIG9ubHkgZG8gaXQgZm9yIGRlZmluZSgpJ2QgIG1vZHVsZXMuIHJlcXVpcmUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBlcnJiYWNrcyBzaG91bGQgbm90IGJlIGNhbGxlZCBmb3IgZmFpbHVyZXMgaW4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyB0aGVpciBjYWxsYmFja3MgKCM2OTkpLiBIb3dldmVyIGlmIGEgZ2xvYmFsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gb25FcnJvciBpcyBzZXQsIHVzZSB0aGF0LgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmICgodGhpcy5ldmVudHMuZXJyb3IgJiYgdGhpcy5tYXAuaXNEZWZpbmUpIHx8CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcS5vbkVycm9yICE9PSBkZWZhdWx0T25FcnJvcikgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlcnIucmVxdWlyZU1hcCA9IHRoaXMubWFwOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlcnIucmVxdWlyZU1vZHVsZXMgPSB0aGlzLm1hcC5pc0RlZmluZSA/IFt0aGlzLm1hcC5pZF0gOiBudWxsOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlcnIucmVxdWlyZVR5cGUgPSB0aGlzLm1hcC5pc0RlZmluZSA/ICdkZWZpbmUnIDogJ3JlcXVpcmUnOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gb25FcnJvcigodGhpcy5lcnJvciA9IGVycikpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAodHlwZW9mIGNvbnNvbGUgIT09ICd1bmRlZmluZWQnICYmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIExvZyB0aGUgZXJyb3IgZm9yIGRlYnVnZ2luZy4gSWYgcHJvbWlzZXMgY291bGQgYmUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gdXNlZCwgdGhpcyB3b3VsZCBiZSBkaWZmZXJlbnQsIGJ1dCBtYWtpbmcgZG8uCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoZXJyKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBEbyBub3Qgd2FudCB0byBjb21wbGV0ZWx5IGxvc2UgdGhlIGVycm9yLiBXaGlsZSB0aGlzCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIHdpbGwgbWVzcyB1cCBwcm9jZXNzaW5nIGFuZCBsZWFkIHRvIHNpbWlsYXIgcmVzdWx0cwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBhcyBidWcgMTQ0MCwgaXQgYXQgbGVhc3Qgc3VyZmFjZXMgdGhlIGVycm9yLgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXEub25FcnJvcihlcnIpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vSnVzdCBhIGxpdGVyYWwgdmFsdWUKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4cG9ydHMgPSBmYWN0b3J5OwogICAgICAgICAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmV4cG9ydHMgPSBleHBvcnRzOwoKICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHRoaXMubWFwLmlzRGVmaW5lICYmICF0aGlzLmlnbm9yZSkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVmaW5lZFtpZF0gPSBleHBvcnRzOwoKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIChyZXEub25SZXNvdXJjZUxvYWQpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXIgcmVzTG9hZE1hcHMgPSBbXTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlYWNoKHRoaXMuZGVwTWFwcywgZnVuY3Rpb24gKGRlcE1hcCkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXNMb2FkTWFwcy5wdXNoKGRlcE1hcC5ub3JtYWxpemVkTWFwIHx8IGRlcE1hcCk7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSk7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVxLm9uUmVzb3VyY2VMb2FkKGNvbnRleHQsIHRoaXMubWFwLCByZXNMb2FkTWFwcyk7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICAgICAgICAgIC8vQ2xlYW4gdXAKICAgICAgICAgICAgICAgICAgICAgICAgY2xlYW5SZWdpc3RyeShpZCk7CgogICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmRlZmluZWQgPSB0cnVlOwogICAgICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICAgICAgLy9GaW5pc2hlZCB0aGUgZGVmaW5lIHN0YWdlLiBBbGxvdyBjYWxsaW5nIGNoZWNrIGFnYWluCiAgICAgICAgICAgICAgICAgICAgLy90byBhbGxvdyBkZWZpbmUgbm90aWZpY2F0aW9ucyBiZWxvdyBpbiB0aGUgY2FzZSBvZiBhCiAgICAgICAgICAgICAgICAgICAgLy9jeWNsZS4KICAgICAgICAgICAgICAgICAgICB0aGlzLmRlZmluaW5nID0gZmFsc2U7CgogICAgICAgICAgICAgICAgICAgIGlmICh0aGlzLmRlZmluZWQgJiYgIXRoaXMuZGVmaW5lRW1pdHRlZCkgewogICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmRlZmluZUVtaXR0ZWQgPSB0cnVlOwogICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmVtaXQoJ2RlZmluZWQnLCB0aGlzLmV4cG9ydHMpOwogICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmRlZmluZUVtaXRDb21wbGV0ZSA9IHRydWU7CiAgICAgICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKCiAgICAgICAgICAgIGNhbGxQbHVnaW46IGZ1bmN0aW9uICgpIHsKICAgICAgICAgICAgICAgIHZhciBtYXAgPSB0aGlzLm1hcCwKICAgICAgICAgICAgICAgICAgICBpZCA9IG1hcC5pZCwKICAgICAgICAgICAgICAgICAgICAvL01hcCBhbHJlYWR5IG5vcm1hbGl6ZWQgdGhlIHByZWZpeC4KICAgICAgICAgICAgICAgICAgICBwbHVnaW5NYXAgPSBtYWtlTW9kdWxlTWFwKG1hcC5wcmVmaXgpOwoKICAgICAgICAgICAgICAgIC8vTWFyayB0aGlzIGFzIGEgZGVwZW5kZW5jeSBmb3IgdGhpcyBwbHVnaW4sIHNvIGl0CiAgICAgICAgICAgICAgICAvL2NhbiBiZSB0cmFjZWQgZm9yIGN5Y2xlcy4KICAgICAgICAgICAgICAgIHRoaXMuZGVwTWFwcy5wdXNoKHBsdWdpbk1hcCk7CgogICAgICAgICAgICAgICAgb24ocGx1Z2luTWFwLCAnZGVmaW5lZCcsIGJpbmQodGhpcywgZnVuY3Rpb24gKHBsdWdpbikgewogICAgICAgICAgICAgICAgICAgIHZhciBsb2FkLCBub3JtYWxpemVkTWFwLCBub3JtYWxpemVkTW9kLAogICAgICAgICAgICAgICAgICAgICAgICBidW5kbGVJZCA9IGdldE93bihidW5kbGVzTWFwLCB0aGlzLm1hcC5pZCksCiAgICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSB0aGlzLm1hcC5uYW1lLAogICAgICAgICAgICAgICAgICAgICAgICBwYXJlbnROYW1lID0gdGhpcy5tYXAucGFyZW50TWFwID8gdGhpcy5tYXAucGFyZW50TWFwLm5hbWUgOiBudWxsLAogICAgICAgICAgICAgICAgICAgICAgICBsb2NhbFJlcXVpcmUgPSBjb250ZXh0Lm1ha2VSZXF1aXJlKG1hcC5wYXJlbnRNYXAsIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVuYWJsZUJ1aWxkQ2FsbGJhY2s6IHRydWUKICAgICAgICAgICAgICAgICAgICAgICAgfSk7CgogICAgICAgICAgICAgICAgICAgIC8vSWYgY3VycmVudCBtYXAgaXMgbm90IG5vcm1hbGl6ZWQsIHdhaXQgZm9yIHRoYXQKICAgICAgICAgICAgICAgICAgICAvL25vcm1hbGl6ZWQgbmFtZSB0byBsb2FkIGluc3RlYWQgb2YgY29udGludWluZy4KICAgICAgICAgICAgICAgICAgICBpZiAodGhpcy5tYXAudW5ub3JtYWxpemVkKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIC8vTm9ybWFsaXplIHRoZSBJRCBpZiB0aGUgcGx1Z2luIGFsbG93cyBpdC4KICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHBsdWdpbi5ub3JtYWxpemUpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSBwbHVnaW4ubm9ybWFsaXplKG5hbWUsIGZ1bmN0aW9uIChuYW1lKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIG5vcm1hbGl6ZShuYW1lLCBwYXJlbnROYW1lLCB0cnVlKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0pIHx8ICcnOwogICAgICAgICAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgICAgICAgICAvL3ByZWZpeCBhbmQgbmFtZSBzaG91bGQgYWxyZWFkeSBiZSBub3JtYWxpemVkLCBubyBuZWVkCiAgICAgICAgICAgICAgICAgICAgICAgIC8vZm9yIGFwcGx5aW5nIG1hcCBjb25maWcgYWdhaW4gZWl0aGVyLgogICAgICAgICAgICAgICAgICAgICAgICBub3JtYWxpemVkTWFwID0gbWFrZU1vZHVsZU1hcChtYXAucHJlZml4ICsgJyEnICsgbmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5tYXAucGFyZW50TWFwKTsKICAgICAgICAgICAgICAgICAgICAgICAgb24obm9ybWFsaXplZE1hcCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICdkZWZpbmVkJywgYmluZCh0aGlzLCBmdW5jdGlvbiAodmFsdWUpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGlzLm1hcC5ub3JtYWxpemVkTWFwID0gbm9ybWFsaXplZE1hcDsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmluaXQoW10sIGZ1bmN0aW9uICgpIHsgcmV0dXJuIHZhbHVlOyB9LCBudWxsLCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVuYWJsZWQ6IHRydWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlnbm9yZTogdHJ1ZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0pOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgfSkpOwoKICAgICAgICAgICAgICAgICAgICAgICAgbm9ybWFsaXplZE1vZCA9IGdldE93bihyZWdpc3RyeSwgbm9ybWFsaXplZE1hcC5pZCk7CiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChub3JtYWxpemVkTW9kKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvL01hcmsgdGhpcyBhcyBhIGRlcGVuZGVuY3kgZm9yIHRoaXMgcGx1Z2luLCBzbyBpdAogICAgICAgICAgICAgICAgICAgICAgICAgICAgLy9jYW4gYmUgdHJhY2VkIGZvciBjeWNsZXMuCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmRlcE1hcHMucHVzaChub3JtYWxpemVkTWFwKTsKCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAodGhpcy5ldmVudHMuZXJyb3IpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBub3JtYWxpemVkTW9kLm9uKCdlcnJvcicsIGJpbmQodGhpcywgZnVuY3Rpb24gKGVycikgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmVtaXQoJ2Vycm9yJywgZXJyKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9KSk7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBub3JtYWxpemVkTW9kLmVuYWJsZSgpOwogICAgICAgICAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgICAgICAgICByZXR1cm47CiAgICAgICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgICAgICAvL0lmIGEgcGF0aHMgY29uZmlnLCB0aGVuIGp1c3QgbG9hZCB0aGF0IGZpbGUgaW5zdGVhZCB0bwogICAgICAgICAgICAgICAgICAgIC8vcmVzb2x2ZSB0aGUgcGx1Z2luLCBhcyBpdCBpcyBidWlsdCBpbnRvIHRoYXQgcGF0aHMgbGF5ZXIuCiAgICAgICAgICAgICAgICAgICAgaWYgKGJ1bmRsZUlkKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMubWFwLnVybCA9IGNvbnRleHQubmFtZVRvVXJsKGJ1bmRsZUlkKTsKICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5sb2FkKCk7CiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybjsKICAgICAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgICAgIGxvYWQgPSBiaW5kKHRoaXMsIGZ1bmN0aW9uICh2YWx1ZSkgewogICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmluaXQoW10sIGZ1bmN0aW9uICgpIHsgcmV0dXJuIHZhbHVlOyB9LCBudWxsLCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbmFibGVkOiB0cnVlCiAgICAgICAgICAgICAgICAgICAgICAgIH0pOwogICAgICAgICAgICAgICAgICAgIH0pOwoKICAgICAgICAgICAgICAgICAgICBsb2FkLmVycm9yID0gYmluZCh0aGlzLCBmdW5jdGlvbiAoZXJyKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuaW5pdGVkID0gdHJ1ZTsKICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5lcnJvciA9IGVycjsKICAgICAgICAgICAgICAgICAgICAgICAgZXJyLnJlcXVpcmVNb2R1bGVzID0gW2lkXTsKCiAgICAgICAgICAgICAgICAgICAgICAgIC8vUmVtb3ZlIHRlbXAgdW5ub3JtYWxpemVkIG1vZHVsZXMgZm9yIHRoaXMgbW9kdWxlLAogICAgICAgICAgICAgICAgICAgICAgICAvL3NpbmNlIHRoZXkgd2lsbCBuZXZlciBiZSByZXNvbHZlZCBvdGhlcndpc2Ugbm93LgogICAgICAgICAgICAgICAgICAgICAgICBlYWNoUHJvcChyZWdpc3RyeSwgZnVuY3Rpb24gKG1vZCkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKG1vZC5tYXAuaWQuaW5kZXhPZihpZCArICdfdW5ub3JtYWxpemVkJykgPT09IDApIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbGVhblJlZ2lzdHJ5KG1vZC5tYXAuaWQpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9KTsKCiAgICAgICAgICAgICAgICAgICAgICAgIG9uRXJyb3IoZXJyKTsKICAgICAgICAgICAgICAgICAgICB9KTsKCiAgICAgICAgICAgICAgICAgICAgLy9BbGxvdyBwbHVnaW5zIHRvIGxvYWQgb3RoZXIgY29kZSB3aXRob3V0IGhhdmluZyB0byBrbm93IHRoZQogICAgICAgICAgICAgICAgICAgIC8vY29udGV4dCBvciBob3cgdG8gJ2NvbXBsZXRlJyB0aGUgbG9hZC4KICAgICAgICAgICAgICAgICAgICBsb2FkLmZyb21UZXh0ID0gYmluZCh0aGlzLCBmdW5jdGlvbiAodGV4dCwgdGV4dEFsdCkgewogICAgICAgICAgICAgICAgICAgICAgICAvKmpzbGludCBldmlsOiB0cnVlICovCiAgICAgICAgICAgICAgICAgICAgICAgIHZhciBtb2R1bGVOYW1lID0gbWFwLm5hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtb2R1bGVNYXAgPSBtYWtlTW9kdWxlTWFwKG1vZHVsZU5hbWUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgaGFzSW50ZXJhY3RpdmUgPSB1c2VJbnRlcmFjdGl2ZTsKCiAgICAgICAgICAgICAgICAgICAgICAgIC8vQXMgb2YgMi4xLjAsIHN1cHBvcnQganVzdCBwYXNzaW5nIHRoZSB0ZXh0LCB0byByZWluZm9yY2UKICAgICAgICAgICAgICAgICAgICAgICAgLy9mcm9tVGV4dCBvbmx5IGJlaW5nIGNhbGxlZCBvbmNlIHBlciByZXNvdXJjZS4gU3RpbGwKICAgICAgICAgICAgICAgICAgICAgICAgLy9zdXBwb3J0IG9sZCBzdHlsZSBvZiBwYXNzaW5nIG1vZHVsZU5hbWUgYnV0IGRpc2NhcmQKICAgICAgICAgICAgICAgICAgICAgICAgLy90aGF0IG1vZHVsZU5hbWUgaW4gZmF2b3Igb2YgdGhlIGludGVybmFsIHJlZi4KICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHRleHRBbHQpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRleHQgPSB0ZXh0QWx0OwogICAgICAgICAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgICAgICAgICAvL1R1cm4gb2ZmIGludGVyYWN0aXZlIHNjcmlwdCBtYXRjaGluZyBmb3IgSUUgZm9yIGFueSBkZWZpbmUKICAgICAgICAgICAgICAgICAgICAgICAgLy9jYWxscyBpbiB0aGUgdGV4dCwgdGhlbiB0dXJuIGl0IGJhY2sgb24gYXQgdGhlIGVuZC4KICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGhhc0ludGVyYWN0aXZlKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB1c2VJbnRlcmFjdGl2ZSA9IGZhbHNlOwogICAgICAgICAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgICAgICAgICAvL1ByaW1lIHRoZSBzeXN0ZW0gYnkgY3JlYXRpbmcgYSBtb2R1bGUgaW5zdGFuY2UgZm9yCiAgICAgICAgICAgICAgICAgICAgICAgIC8vaXQuCiAgICAgICAgICAgICAgICAgICAgICAgIGdldE1vZHVsZShtb2R1bGVNYXApOwoKICAgICAgICAgICAgICAgICAgICAgICAgLy9UcmFuc2ZlciBhbnkgY29uZmlnIHRvIHRoaXMgb3RoZXIgbW9kdWxlLgogICAgICAgICAgICAgICAgICAgICAgICBpZiAoaGFzUHJvcChjb25maWcuY29uZmlnLCBpZCkpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbmZpZy5jb25maWdbbW9kdWxlTmFtZV0gPSBjb25maWcuY29uZmlnW2lkXTsKICAgICAgICAgICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgICAgICAgICAgdHJ5IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcS5leGVjKHRleHQpOwogICAgICAgICAgICAgICAgICAgICAgICB9IGNhdGNoIChlKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gb25FcnJvcihtYWtlRXJyb3IoJ2Zyb210ZXh0ZXZhbCcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdmcm9tVGV4dCBldmFsIGZvciAnICsgaWQgKwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICcgZmFpbGVkOiAnICsgZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgW2lkXSkpOwogICAgICAgICAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgICAgICAgICBpZiAoaGFzSW50ZXJhY3RpdmUpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVzZUludGVyYWN0aXZlID0gdHJ1ZTsKICAgICAgICAgICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgICAgICAgICAgLy9NYXJrIHRoaXMgYXMgYSBkZXBlbmRlbmN5IGZvciB0aGUgcGx1Z2luCiAgICAgICAgICAgICAgICAgICAgICAgIC8vcmVzb3VyY2UKICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5kZXBNYXBzLnB1c2gobW9kdWxlTWFwKTsKCiAgICAgICAgICAgICAgICAgICAgICAgIC8vU3VwcG9ydCBhbm9ueW1vdXMgbW9kdWxlcy4KICAgICAgICAgICAgICAgICAgICAgICAgY29udGV4dC5jb21wbGV0ZUxvYWQobW9kdWxlTmFtZSk7CgogICAgICAgICAgICAgICAgICAgICAgICAvL0JpbmQgdGhlIHZhbHVlIG9mIHRoYXQgbW9kdWxlIHRvIHRoZSB2YWx1ZSBmb3IgdGhpcwogICAgICAgICAgICAgICAgICAgICAgICAvL3Jlc291cmNlIElELgogICAgICAgICAgICAgICAgICAgICAgICBsb2NhbFJlcXVpcmUoW21vZHVsZU5hbWVdLCBsb2FkKTsKICAgICAgICAgICAgICAgICAgICB9KTsKCiAgICAgICAgICAgICAgICAgICAgLy9Vc2UgcGFyZW50TmFtZSBoZXJlIHNpbmNlIHRoZSBwbHVnaW4ncyBuYW1lIGlzIG5vdCByZWxpYWJsZSwKICAgICAgICAgICAgICAgICAgICAvL2NvdWxkIGJlIHNvbWUgd2VpcmQgc3RyaW5nIHdpdGggbm8gcGF0aCB0aGF0IGFjdHVhbGx5IHdhbnRzIHRvCiAgICAgICAgICAgICAgICAgICAgLy9yZWZlcmVuY2UgdGhlIHBhcmVudE5hbWUncyBwYXRoLgogICAgICAgICAgICAgICAgICAgIHBsdWdpbi5sb2FkKG1hcC5uYW1lLCBsb2NhbFJlcXVpcmUsIGxvYWQsIGNvbmZpZyk7CiAgICAgICAgICAgICAgICB9KSk7CgogICAgICAgICAgICAgICAgY29udGV4dC5lbmFibGUocGx1Z2luTWFwLCB0aGlzKTsKICAgICAgICAgICAgICAgIHRoaXMucGx1Z2luTWFwc1twbHVnaW5NYXAuaWRdID0gcGx1Z2luTWFwOwogICAgICAgICAgICB9LAoKICAgICAgICAgICAgZW5hYmxlOiBmdW5jdGlvbiAoKSB7CiAgICAgICAgICAgICAgICBlbmFibGVkUmVnaXN0cnlbdGhpcy5tYXAuaWRdID0gdGhpczsKICAgICAgICAgICAgICAgIHRoaXMuZW5hYmxlZCA9IHRydWU7CgogICAgICAgICAgICAgICAgLy9TZXQgZmxhZyBtZW50aW9uaW5nIHRoYXQgdGhlIG1vZHVsZSBpcyBlbmFibGluZywKICAgICAgICAgICAgICAgIC8vc28gdGhhdCBpbW1lZGlhdGUgY2FsbHMgdG8gdGhlIGRlZmluZWQgY2FsbGJhY2tzCiAgICAgICAgICAgICAgICAvL2ZvciBkZXBlbmRlbmNpZXMgZG8gbm90IHRyaWdnZXIgaW5hZHZlcnRlbnQgbG9hZAogICAgICAgICAgICAgICAgLy93aXRoIHRoZSBkZXBDb3VudCBzdGlsbCBiZWluZyB6ZXJvLgogICAgICAgICAgICAgICAgdGhpcy5lbmFibGluZyA9IHRydWU7CgogICAgICAgICAgICAgICAgLy9FbmFibGUgZWFjaCBkZXBlbmRlbmN5CiAgICAgICAgICAgICAgICBlYWNoKHRoaXMuZGVwTWFwcywgYmluZCh0aGlzLCBmdW5jdGlvbiAoZGVwTWFwLCBpKSB7CiAgICAgICAgICAgICAgICAgICAgdmFyIGlkLCBtb2QsIGhhbmRsZXI7CgogICAgICAgICAgICAgICAgICAgIGlmICh0eXBlb2YgZGVwTWFwID09PSAnc3RyaW5nJykgewogICAgICAgICAgICAgICAgICAgICAgICAvL0RlcGVuZGVuY3kgbmVlZHMgdG8gYmUgY29udmVydGVkIHRvIGEgZGVwTWFwCiAgICAgICAgICAgICAgICAgICAgICAgIC8vYW5kIHdpcmVkIHVwIHRvIHRoaXMgbW9kdWxlLgogICAgICAgICAgICAgICAgICAgICAgICBkZXBNYXAgPSBtYWtlTW9kdWxlTWFwKGRlcE1hcCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAodGhpcy5tYXAuaXNEZWZpbmUgPyB0aGlzLm1hcCA6IHRoaXMubWFwLnBhcmVudE1hcCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFsc2UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIXRoaXMuc2tpcE1hcCk7CiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuZGVwTWFwc1tpXSA9IGRlcE1hcDsKCiAgICAgICAgICAgICAgICAgICAgICAgIGhhbmRsZXIgPSBnZXRPd24oaGFuZGxlcnMsIGRlcE1hcC5pZCk7CgogICAgICAgICAgICAgICAgICAgICAgICBpZiAoaGFuZGxlcikgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5kZXBFeHBvcnRzW2ldID0gaGFuZGxlcih0aGlzKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybjsKICAgICAgICAgICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5kZXBDb3VudCArPSAxOwoKICAgICAgICAgICAgICAgICAgICAgICAgb24oZGVwTWFwLCAnZGVmaW5lZCcsIGJpbmQodGhpcywgZnVuY3Rpb24gKGRlcEV4cG9ydHMpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmICh0aGlzLnVuZGVmZWQpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm47CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmRlZmluZURlcChpLCBkZXBFeHBvcnRzKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuY2hlY2soKTsKICAgICAgICAgICAgICAgICAgICAgICAgfSkpOwoKICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHRoaXMuZXJyYmFjaykgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgb24oZGVwTWFwLCAnZXJyb3InLCBiaW5kKHRoaXMsIHRoaXMuZXJyYmFjaykpOwogICAgICAgICAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKHRoaXMuZXZlbnRzLmVycm9yKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBObyBkaXJlY3QgZXJyYmFjayBvbiB0aGlzIG1vZHVsZSwgYnV0IHNvbWV0aGluZwogICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gZWxzZSBpcyBsaXN0ZW5pbmcgZm9yIGVycm9ycywgc28gYmUgc3VyZSB0bwogICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gcHJvcGFnYXRlIHRoZSBlcnJvciBjb3JyZWN0bHkuCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBvbihkZXBNYXAsICdlcnJvcicsIGJpbmQodGhpcywgZnVuY3Rpb24oZXJyKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5lbWl0KCdlcnJvcicsIGVycik7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9KSk7CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgICAgIGlkID0gZGVwTWFwLmlkOwogICAgICAgICAgICAgICAgICAgIG1vZCA9IHJlZ2lzdHJ5W2lkXTsKCiAgICAgICAgICAgICAgICAgICAgLy9Ta2lwIHNwZWNpYWwgbW9kdWxlcyBsaWtlICdyZXF1aXJlJywgJ2V4cG9ydHMnLCAnbW9kdWxlJwogICAgICAgICAgICAgICAgICAgIC8vQWxzbywgZG9uJ3QgY2FsbCBlbmFibGUgaWYgaXQgaXMgYWxyZWFkeSBlbmFibGVkLAogICAgICAgICAgICAgICAgICAgIC8vaW1wb3J0YW50IGluIGNpcmN1bGFyIGRlcGVuZGVuY3kgY2FzZXMuCiAgICAgICAgICAgICAgICAgICAgaWYgKCFoYXNQcm9wKGhhbmRsZXJzLCBpZCkgJiYgbW9kICYmICFtb2QuZW5hYmxlZCkgewogICAgICAgICAgICAgICAgICAgICAgICBjb250ZXh0LmVuYWJsZShkZXBNYXAsIHRoaXMpOwogICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIH0pKTsKCiAgICAgICAgICAgICAgICAvL0VuYWJsZSBlYWNoIHBsdWdpbiB0aGF0IGlzIHVzZWQgaW4KICAgICAgICAgICAgICAgIC8vYSBkZXBlbmRlbmN5CiAgICAgICAgICAgICAgICBlYWNoUHJvcCh0aGlzLnBsdWdpbk1hcHMsIGJpbmQodGhpcywgZnVuY3Rpb24gKHBsdWdpbk1hcCkgewogICAgICAgICAgICAgICAgICAgIHZhciBtb2QgPSBnZXRPd24ocmVnaXN0cnksIHBsdWdpbk1hcC5pZCk7CiAgICAgICAgICAgICAgICAgICAgaWYgKG1vZCAmJiAhbW9kLmVuYWJsZWQpIHsKICAgICAgICAgICAgICAgICAgICAgICAgY29udGV4dC5lbmFibGUocGx1Z2luTWFwLCB0aGlzKTsKICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICB9KSk7CgogICAgICAgICAgICAgICAgdGhpcy5lbmFibGluZyA9IGZhbHNlOwoKICAgICAgICAgICAgICAgIHRoaXMuY2hlY2soKTsKICAgICAgICAgICAgfSwKCiAgICAgICAgICAgIG9uOiBmdW5jdGlvbiAobmFtZSwgY2IpIHsKICAgICAgICAgICAgICAgIHZhciBjYnMgPSB0aGlzLmV2ZW50c1tuYW1lXTsKICAgICAgICAgICAgICAgIGlmICghY2JzKSB7CiAgICAgICAgICAgICAgICAgICAgY2JzID0gdGhpcy5ldmVudHNbbmFtZV0gPSBbXTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIGNicy5wdXNoKGNiKTsKICAgICAgICAgICAgfSwKCiAgICAgICAgICAgIGVtaXQ6IGZ1bmN0aW9uIChuYW1lLCBldnQpIHsKICAgICAgICAgICAgICAgIGVhY2godGhpcy5ldmVudHNbbmFtZV0sIGZ1bmN0aW9uIChjYikgewogICAgICAgICAgICAgICAgICAgIGNiKGV2dCk7CiAgICAgICAgICAgICAgICB9KTsKICAgICAgICAgICAgICAgIGlmIChuYW1lID09PSAnZXJyb3InKSB7CiAgICAgICAgICAgICAgICAgICAgLy9Ob3cgdGhhdCB0aGUgZXJyb3IgaGFuZGxlciB3YXMgdHJpZ2dlcmVkLCByZW1vdmUKICAgICAgICAgICAgICAgICAgICAvL3RoZSBsaXN0ZW5lcnMsIHNpbmNlIHRoaXMgYnJva2VuIE1vZHVsZSBpbnN0YW5jZQogICAgICAgICAgICAgICAgICAgIC8vY2FuIHN0YXkgYXJvdW5kIGZvciBhIHdoaWxlIGluIHRoZSByZWdpc3RyeS4KICAgICAgICAgICAgICAgICAgICBkZWxldGUgdGhpcy5ldmVudHNbbmFtZV07CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICB9OwoKICAgICAgICBmdW5jdGlvbiBjYWxsR2V0TW9kdWxlKGFyZ3MpIHsKICAgICAgICAgICAgLy9Ta2lwIG1vZHVsZXMgYWxyZWFkeSBkZWZpbmVkLgogICAgICAgICAgICBpZiAoIWhhc1Byb3AoZGVmaW5lZCwgYXJnc1swXSkpIHsKICAgICAgICAgICAgICAgIGdldE1vZHVsZShtYWtlTW9kdWxlTWFwKGFyZ3NbMF0sIG51bGwsIHRydWUpKS5pbml0KGFyZ3NbMV0sIGFyZ3NbMl0pOwogICAgICAgICAgICB9CiAgICAgICAgfQoKICAgICAgICBmdW5jdGlvbiByZW1vdmVMaXN0ZW5lcihub2RlLCBmdW5jLCBuYW1lLCBpZU5hbWUpIHsKICAgICAgICAgICAgLy9GYXZvciBkZXRhY2hFdmVudCBiZWNhdXNlIG9mIElFOQogICAgICAgICAgICAvL2lzc3VlLCBzZWUgYXR0YWNoRXZlbnQvYWRkRXZlbnRMaXN0ZW5lciBjb21tZW50IGVsc2V3aGVyZQogICAgICAgICAgICAvL2luIHRoaXMgZmlsZS4KICAgICAgICAgICAgaWYgKG5vZGUuZGV0YWNoRXZlbnQgJiYgIWlzT3BlcmEpIHsKICAgICAgICAgICAgICAgIC8vUHJvYmFibHkgSUUuIElmIG5vdCBpdCB3aWxsIHRocm93IGFuIGVycm9yLCB3aGljaCB3aWxsIGJlCiAgICAgICAgICAgICAgICAvL3VzZWZ1bCB0byBrbm93LgogICAgICAgICAgICAgICAgaWYgKGllTmFtZSkgewogICAgICAgICAgICAgICAgICAgIG5vZGUuZGV0YWNoRXZlbnQoaWVOYW1lLCBmdW5jKTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgICAgIG5vZGUucmVtb3ZlRXZlbnRMaXN0ZW5lcihuYW1lLCBmdW5jLCBmYWxzZSk7CiAgICAgICAgICAgIH0KICAgICAgICB9CgogICAgICAgIC8qKgogICAgICAgICAqIEdpdmVuIGFuIGV2ZW50IGZyb20gYSBzY3JpcHQgbm9kZSwgZ2V0IHRoZSByZXF1aXJlanMgaW5mbyBmcm9tIGl0LAogICAgICAgICAqIGFuZCB0aGVuIHJlbW92ZXMgdGhlIGV2ZW50IGxpc3RlbmVycyBvbiB0aGUgbm9kZS4KICAgICAgICAgKiBAcGFyYW0ge0V2ZW50fSBldnQKICAgICAgICAgKiBAcmV0dXJucyB7T2JqZWN0fQogICAgICAgICAqLwogICAgICAgIGZ1bmN0aW9uIGdldFNjcmlwdERhdGEoZXZ0KSB7CiAgICAgICAgICAgIC8vVXNpbmcgY3VycmVudFRhcmdldCBpbnN0ZWFkIG9mIHRhcmdldCBmb3IgRmlyZWZveCAyLjAncyBzYWtlLiBOb3QKICAgICAgICAgICAgLy9hbGwgb2xkIGJyb3dzZXJzIHdpbGwgYmUgc3VwcG9ydGVkLCBidXQgdGhpcyBvbmUgd2FzIGVhc3kgZW5vdWdoCiAgICAgICAgICAgIC8vdG8gc3VwcG9ydCBhbmQgc3RpbGwgbWFrZXMgc2Vuc2UuCiAgICAgICAgICAgIHZhciBub2RlID0gZXZ0LmN1cnJlbnRUYXJnZXQgfHwgZXZ0LnNyY0VsZW1lbnQ7CgogICAgICAgICAgICAvL1JlbW92ZSB0aGUgbGlzdGVuZXJzIG9uY2UgaGVyZS4KICAgICAgICAgICAgcmVtb3ZlTGlzdGVuZXIobm9kZSwgY29udGV4dC5vblNjcmlwdExvYWQsICdsb2FkJywgJ29ucmVhZHlzdGF0ZWNoYW5nZScpOwogICAgICAgICAgICByZW1vdmVMaXN0ZW5lcihub2RlLCBjb250ZXh0Lm9uU2NyaXB0RXJyb3IsICdlcnJvcicpOwoKICAgICAgICAgICAgcmV0dXJuIHsKICAgICAgICAgICAgICAgIG5vZGU6IG5vZGUsCiAgICAgICAgICAgICAgICBpZDogbm9kZSAmJiBub2RlLmdldEF0dHJpYnV0ZSgnZGF0YS1yZXF1aXJlbW9kdWxlJykKICAgICAgICAgICAgfTsKICAgICAgICB9CgogICAgICAgIGZ1bmN0aW9uIGludGFrZURlZmluZXMoKSB7CiAgICAgICAgICAgIHZhciBhcmdzOwoKICAgICAgICAgICAgLy9BbnkgZGVmaW5lZCBtb2R1bGVzIGluIHRoZSBnbG9iYWwgcXVldWUsIGludGFrZSB0aGVtIG5vdy4KICAgICAgICAgICAgdGFrZUdsb2JhbFF1ZXVlKCk7CgogICAgICAgICAgICAvL01ha2Ugc3VyZSBhbnkgcmVtYWluaW5nIGRlZlF1ZXVlIGl0ZW1zIGdldCBwcm9wZXJseSBwcm9jZXNzZWQuCiAgICAgICAgICAgIHdoaWxlIChkZWZRdWV1ZS5sZW5ndGgpIHsKICAgICAgICAgICAgICAgIGFyZ3MgPSBkZWZRdWV1ZS5zaGlmdCgpOwogICAgICAgICAgICAgICAgaWYgKGFyZ3NbMF0gPT09IG51bGwpIHsKICAgICAgICAgICAgICAgICAgICByZXR1cm4gb25FcnJvcihtYWtlRXJyb3IoJ21pc21hdGNoJywgJ01pc21hdGNoZWQgYW5vbnltb3VzIGRlZmluZSgpIG1vZHVsZTogJyArCiAgICAgICAgICAgICAgICAgICAgICAgIGFyZ3NbYXJncy5sZW5ndGggLSAxXSkpOwogICAgICAgICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgICAgICAgICAvL2FyZ3MgYXJlIGlkLCBkZXBzLCBmYWN0b3J5LiBTaG91bGQgYmUgbm9ybWFsaXplZCBieSB0aGUKICAgICAgICAgICAgICAgICAgICAvL2RlZmluZSgpIGZ1bmN0aW9uLgogICAgICAgICAgICAgICAgICAgIGNhbGxHZXRNb2R1bGUoYXJncyk7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICAgICAgY29udGV4dC5kZWZRdWV1ZU1hcCA9IHt9OwogICAgICAgIH0KCiAgICAgICAgY29udGV4dCA9IHsKICAgICAgICAgICAgY29uZmlnOiBjb25maWcsCiAgICAgICAgICAgIGNvbnRleHROYW1lOiBjb250ZXh0TmFtZSwKICAgICAgICAgICAgcmVnaXN0cnk6IHJlZ2lzdHJ5LAogICAgICAgICAgICBkZWZpbmVkOiBkZWZpbmVkLAogICAgICAgICAgICB1cmxGZXRjaGVkOiB1cmxGZXRjaGVkLAogICAgICAgICAgICBkZWZRdWV1ZTogZGVmUXVldWUsCiAgICAgICAgICAgIGRlZlF1ZXVlTWFwOiB7fSwKICAgICAgICAgICAgTW9kdWxlOiBNb2R1bGUsCiAgICAgICAgICAgIG1ha2VNb2R1bGVNYXA6IG1ha2VNb2R1bGVNYXAsCiAgICAgICAgICAgIG5leHRUaWNrOiByZXEubmV4dFRpY2ssCiAgICAgICAgICAgIG9uRXJyb3I6IG9uRXJyb3IsCgogICAgICAgICAgICAvKioKICAgICAgICAgICAgICogU2V0IGEgY29uZmlndXJhdGlvbiBmb3IgdGhlIGNvbnRleHQuCiAgICAgICAgICAgICAqIEBwYXJhbSB7T2JqZWN0fSBjZmcgY29uZmlnIG9iamVjdCB0byBpbnRlZ3JhdGUuCiAgICAgICAgICAgICAqLwogICAgICAgICAgICBjb25maWd1cmU6IGZ1bmN0aW9uIChjZmcpIHsKICAgICAgICAgICAgICAgIC8vTWFrZSBzdXJlIHRoZSBiYXNlVXJsIGVuZHMgaW4gYSBzbGFzaC4KICAgICAgICAgICAgICAgIGlmIChjZmcuYmFzZVVybCkgewogICAgICAgICAgICAgICAgICAgIGlmIChjZmcuYmFzZVVybC5jaGFyQXQoY2ZnLmJhc2VVcmwubGVuZ3RoIC0gMSkgIT09ICcvJykgewogICAgICAgICAgICAgICAgICAgICAgICBjZmcuYmFzZVVybCArPSAnLyc7CiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgIC8vU2F2ZSBvZmYgdGhlIHBhdGhzIHNpbmNlIHRoZXkgcmVxdWlyZSBzcGVjaWFsIHByb2Nlc3NpbmcsCiAgICAgICAgICAgICAgICAvL3RoZXkgYXJlIGFkZGl0aXZlLgogICAgICAgICAgICAgICAgdmFyIHNoaW0gPSBjb25maWcuc2hpbSwKICAgICAgICAgICAgICAgICAgICBvYmpzID0gewogICAgICAgICAgICAgICAgICAgICAgICBwYXRoczogdHJ1ZSwKICAgICAgICAgICAgICAgICAgICAgICAgYnVuZGxlczogdHJ1ZSwKICAgICAgICAgICAgICAgICAgICAgICAgY29uZmlnOiB0cnVlLAogICAgICAgICAgICAgICAgICAgICAgICBtYXA6IHRydWUKICAgICAgICAgICAgICAgICAgICB9OwoKICAgICAgICAgICAgICAgIGVhY2hQcm9wKGNmZywgZnVuY3Rpb24gKHZhbHVlLCBwcm9wKSB7CiAgICAgICAgICAgICAgICAgICAgaWYgKG9ianNbcHJvcF0pIHsKICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCFjb25maWdbcHJvcF0pIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbmZpZ1twcm9wXSA9IHt9OwogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIG1peGluKGNvbmZpZ1twcm9wXSwgdmFsdWUsIHRydWUsIHRydWUpOwogICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgICAgICAgICAgICAgIGNvbmZpZ1twcm9wXSA9IHZhbHVlOwogICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIH0pOwoKICAgICAgICAgICAgICAgIC8vUmV2ZXJzZSBtYXAgdGhlIGJ1bmRsZXMKICAgICAgICAgICAgICAgIGlmIChjZmcuYnVuZGxlcykgewogICAgICAgICAgICAgICAgICAgIGVhY2hQcm9wKGNmZy5idW5kbGVzLCBmdW5jdGlvbiAodmFsdWUsIHByb3ApIHsKICAgICAgICAgICAgICAgICAgICAgICAgZWFjaCh2YWx1ZSwgZnVuY3Rpb24gKHYpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmICh2ICE9PSBwcm9wKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnVuZGxlc01hcFt2XSA9IHByb3A7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0pOwogICAgICAgICAgICAgICAgICAgIH0pOwogICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgIC8vTWVyZ2Ugc2hpbQogICAgICAgICAgICAgICAgaWYgKGNmZy5zaGltKSB7CiAgICAgICAgICAgICAgICAgICAgZWFjaFByb3AoY2ZnLnNoaW0sIGZ1bmN0aW9uICh2YWx1ZSwgaWQpIHsKICAgICAgICAgICAgICAgICAgICAgICAgLy9Ob3JtYWxpemUgdGhlIHN0cnVjdHVyZQogICAgICAgICAgICAgICAgICAgICAgICBpZiAoaXNBcnJheSh2YWx1ZSkpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlID0gewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlcHM6IHZhbHVlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9OwogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIGlmICgodmFsdWUuZXhwb3J0cyB8fCB2YWx1ZS5pbml0KSAmJiAhdmFsdWUuZXhwb3J0c0ZuKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZS5leHBvcnRzRm4gPSBjb250ZXh0Lm1ha2VTaGltRXhwb3J0cyh2YWx1ZSk7CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgc2hpbVtpZF0gPSB2YWx1ZTsKICAgICAgICAgICAgICAgICAgICB9KTsKICAgICAgICAgICAgICAgICAgICBjb25maWcuc2hpbSA9IHNoaW07CiAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgLy9BZGp1c3QgcGFja2FnZXMgaWYgbmVjZXNzYXJ5LgogICAgICAgICAgICAgICAgaWYgKGNmZy5wYWNrYWdlcykgewogICAgICAgICAgICAgICAgICAgIGVhY2goY2ZnLnBhY2thZ2VzLCBmdW5jdGlvbiAocGtnT2JqKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIHZhciBsb2NhdGlvbiwgbmFtZTsKCiAgICAgICAgICAgICAgICAgICAgICAgIHBrZ09iaiA9IHR5cGVvZiBwa2dPYmogPT09ICdzdHJpbmcnID8ge25hbWU6IHBrZ09ian0gOiBwa2dPYmo7CgogICAgICAgICAgICAgICAgICAgICAgICBuYW1lID0gcGtnT2JqLm5hbWU7CiAgICAgICAgICAgICAgICAgICAgICAgIGxvY2F0aW9uID0gcGtnT2JqLmxvY2F0aW9uOwogICAgICAgICAgICAgICAgICAgICAgICBpZiAobG9jYXRpb24pIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbmZpZy5wYXRoc1tuYW1lXSA9IHBrZ09iai5sb2NhdGlvbjsKICAgICAgICAgICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgICAgICAgICAgLy9TYXZlIHBvaW50ZXIgdG8gbWFpbiBtb2R1bGUgSUQgZm9yIHBrZyBuYW1lLgogICAgICAgICAgICAgICAgICAgICAgICAvL1JlbW92ZSBsZWFkaW5nIGRvdCBpbiBtYWluLCBzbyBtYWluIHBhdGhzIGFyZSBub3JtYWxpemVkLAogICAgICAgICAgICAgICAgICAgICAgICAvL2FuZCByZW1vdmUgYW55IHRyYWlsaW5nIC5qcywgc2luY2UgZGlmZmVyZW50IHBhY2thZ2UKICAgICAgICAgICAgICAgICAgICAgICAgLy9lbnZzIGhhdmUgZGlmZmVyZW50IGNvbnZlbnRpb25zOiBzb21lIHVzZSBhIG1vZHVsZSBuYW1lLAogICAgICAgICAgICAgICAgICAgICAgICAvL3NvbWUgdXNlIGEgZmlsZSBuYW1lLgogICAgICAgICAgICAgICAgICAgICAgICBjb25maWcucGtnc1tuYW1lXSA9IHBrZ09iai5uYW1lICsgJy8nICsgKHBrZ09iai5tYWluIHx8ICdtYWluJykKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC5yZXBsYWNlKGN1cnJEaXJSZWdFeHAsICcnKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLnJlcGxhY2UoanNTdWZmaXhSZWdFeHAsICcnKTsKICAgICAgICAgICAgICAgICAgICB9KTsKICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICAvL0lmIHRoZXJlIGFyZSBhbnkgIndhaXRpbmcgdG8gZXhlY3V0ZSIgbW9kdWxlcyBpbiB0aGUgcmVnaXN0cnksCiAgICAgICAgICAgICAgICAvL3VwZGF0ZSB0aGUgbWFwcyBmb3IgdGhlbSwgc2luY2UgdGhlaXIgaW5mbywgbGlrZSBVUkxzIHRvIGxvYWQsCiAgICAgICAgICAgICAgICAvL21heSBoYXZlIGNoYW5nZWQuCiAgICAgICAgICAgICAgICBlYWNoUHJvcChyZWdpc3RyeSwgZnVuY3Rpb24gKG1vZCwgaWQpIHsKICAgICAgICAgICAgICAgICAgICAvL0lmIG1vZHVsZSBhbHJlYWR5IGhhcyBpbml0IGNhbGxlZCwgc2luY2UgaXQgaXMgdG9vCiAgICAgICAgICAgICAgICAgICAgLy9sYXRlIHRvIG1vZGlmeSB0aGVtLCBhbmQgaWdub3JlIHVubm9ybWFsaXplZCBvbmVzCiAgICAgICAgICAgICAgICAgICAgLy9zaW5jZSB0aGV5IGFyZSB0cmFuc2llbnQuCiAgICAgICAgICAgICAgICAgICAgaWYgKCFtb2QuaW5pdGVkICYmICFtb2QubWFwLnVubm9ybWFsaXplZCkgewogICAgICAgICAgICAgICAgICAgICAgICBtb2QubWFwID0gbWFrZU1vZHVsZU1hcChpZCwgbnVsbCwgdHJ1ZSk7CiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfSk7CgogICAgICAgICAgICAgICAgLy9JZiBhIGRlcHMgYXJyYXkgb3IgYSBjb25maWcgY2FsbGJhY2sgaXMgc3BlY2lmaWVkLCB0aGVuIGNhbGwKICAgICAgICAgICAgICAgIC8vcmVxdWlyZSB3aXRoIHRob3NlIGFyZ3MuIFRoaXMgaXMgdXNlZnVsIHdoZW4gcmVxdWlyZSBpcyBkZWZpbmVkIGFzIGEKICAgICAgICAgICAgICAgIC8vY29uZmlnIG9iamVjdCBiZWZvcmUgcmVxdWlyZS5qcyBpcyBsb2FkZWQuCiAgICAgICAgICAgICAgICBpZiAoY2ZnLmRlcHMgfHwgY2ZnLmNhbGxiYWNrKSB7CiAgICAgICAgICAgICAgICAgICAgY29udGV4dC5yZXF1aXJlKGNmZy5kZXBzIHx8IFtdLCBjZmcuY2FsbGJhY2spOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAoKICAgICAgICAgICAgbWFrZVNoaW1FeHBvcnRzOiBmdW5jdGlvbiAodmFsdWUpIHsKICAgICAgICAgICAgICAgIGZ1bmN0aW9uIGZuKCkgewogICAgICAgICAgICAgICAgICAgIHZhciByZXQ7CiAgICAgICAgICAgICAgICAgICAgaWYgKHZhbHVlLmluaXQpIHsKICAgICAgICAgICAgICAgICAgICAgICAgcmV0ID0gdmFsdWUuaW5pdC5hcHBseShnbG9iYWwsIGFyZ3VtZW50cyk7CiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgIHJldHVybiByZXQgfHwgKHZhbHVlLmV4cG9ydHMgJiYgZ2V0R2xvYmFsKHZhbHVlLmV4cG9ydHMpKTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIHJldHVybiBmbjsKICAgICAgICAgICAgfSwKCiAgICAgICAgICAgIG1ha2VSZXF1aXJlOiBmdW5jdGlvbiAocmVsTWFwLCBvcHRpb25zKSB7CiAgICAgICAgICAgICAgICBvcHRpb25zID0gb3B0aW9ucyB8fCB7fTsKCiAgICAgICAgICAgICAgICBmdW5jdGlvbiBsb2NhbFJlcXVpcmUoZGVwcywgY2FsbGJhY2ssIGVycmJhY2spIHsKICAgICAgICAgICAgICAgICAgICB2YXIgaWQsIG1hcCwgcmVxdWlyZU1vZDsKCiAgICAgICAgICAgICAgICAgICAgaWYgKG9wdGlvbnMuZW5hYmxlQnVpbGRDYWxsYmFjayAmJiBjYWxsYmFjayAmJiBpc0Z1bmN0aW9uKGNhbGxiYWNrKSkgewogICAgICAgICAgICAgICAgICAgICAgICBjYWxsYmFjay5fX3JlcXVpcmVKc0J1aWxkID0gdHJ1ZTsKICAgICAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgICAgIGlmICh0eXBlb2YgZGVwcyA9PT0gJ3N0cmluZycpIHsKICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGlzRnVuY3Rpb24oY2FsbGJhY2spKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvL0ludmFsaWQgY2FsbAogICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIG9uRXJyb3IobWFrZUVycm9yKCdyZXF1aXJlYXJncycsICdJbnZhbGlkIHJlcXVpcmUgY2FsbCcpLCBlcnJiYWNrKTsKICAgICAgICAgICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgICAgICAgICAgLy9JZiByZXF1aXJlfGV4cG9ydHN8bW9kdWxlIGFyZSByZXF1ZXN0ZWQsIGdldCB0aGUKICAgICAgICAgICAgICAgICAgICAgICAgLy92YWx1ZSBmb3IgdGhlbSBmcm9tIHRoZSBzcGVjaWFsIGhhbmRsZXJzLiBDYXZlYXQ6CiAgICAgICAgICAgICAgICAgICAgICAgIC8vdGhpcyBvbmx5IHdvcmtzIHdoaWxlIG1vZHVsZSBpcyBiZWluZyBkZWZpbmVkLgogICAgICAgICAgICAgICAgICAgICAgICBpZiAocmVsTWFwICYmIGhhc1Byb3AoaGFuZGxlcnMsIGRlcHMpKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gaGFuZGxlcnNbZGVwc10ocmVnaXN0cnlbcmVsTWFwLmlkXSk7CiAgICAgICAgICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICAgICAgICAgIC8vU3luY2hyb25vdXMgYWNjZXNzIHRvIG9uZSBtb2R1bGUuIElmIHJlcXVpcmUuZ2V0IGlzCiAgICAgICAgICAgICAgICAgICAgICAgIC8vYXZhaWxhYmxlIChhcyBpbiB0aGUgTm9kZSBhZGFwdGVyKSwgcHJlZmVyIHRoYXQuCiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChyZXEuZ2V0KSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gcmVxLmdldChjb250ZXh0LCBkZXBzLCByZWxNYXAsIGxvY2FsUmVxdWlyZSk7CiAgICAgICAgICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICAgICAgICAgIC8vTm9ybWFsaXplIG1vZHVsZSBuYW1lLCBpZiBpdCBjb250YWlucyAuIG9yIC4uCiAgICAgICAgICAgICAgICAgICAgICAgIG1hcCA9IG1ha2VNb2R1bGVNYXAoZGVwcywgcmVsTWFwLCBmYWxzZSwgdHJ1ZSk7CiAgICAgICAgICAgICAgICAgICAgICAgIGlkID0gbWFwLmlkOwoKICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCFoYXNQcm9wKGRlZmluZWQsIGlkKSkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIG9uRXJyb3IobWFrZUVycm9yKCdub3Rsb2FkZWQnLCAnTW9kdWxlIG5hbWUgIicgKwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWQgKwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJyIgaGFzIG5vdCBiZWVuIGxvYWRlZCB5ZXQgZm9yIGNvbnRleHQ6ICcgKwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29udGV4dE5hbWUgKwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKHJlbE1hcCA/ICcnIDogJy4gVXNlIHJlcXVpcmUoW10pJykpKTsKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gZGVmaW5lZFtpZF07CiAgICAgICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgICAgICAvL0dyYWIgZGVmaW5lcyB3YWl0aW5nIGluIHRoZSBnbG9iYWwgcXVldWUuCiAgICAgICAgICAgICAgICAgICAgaW50YWtlRGVmaW5lcygpOwoKICAgICAgICAgICAgICAgICAgICAvL01hcmsgYWxsIHRoZSBkZXBlbmRlbmNpZXMgYXMgbmVlZGluZyB0byBiZSBsb2FkZWQuCiAgICAgICAgICAgICAgICAgICAgY29udGV4dC5uZXh0VGljayhmdW5jdGlvbiAoKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIC8vU29tZSBkZWZpbmVzIGNvdWxkIGhhdmUgYmVlbiBhZGRlZCBzaW5jZSB0aGUKICAgICAgICAgICAgICAgICAgICAgICAgLy9yZXF1aXJlIGNhbGwsIGNvbGxlY3QgdGhlbS4KICAgICAgICAgICAgICAgICAgICAgICAgaW50YWtlRGVmaW5lcygpOwoKICAgICAgICAgICAgICAgICAgICAgICAgcmVxdWlyZU1vZCA9IGdldE1vZHVsZShtYWtlTW9kdWxlTWFwKG51bGwsIHJlbE1hcCkpOwoKICAgICAgICAgICAgICAgICAgICAgICAgLy9TdG9yZSBpZiBtYXAgY29uZmlnIHNob3VsZCBiZSBhcHBsaWVkIHRvIHRoaXMgcmVxdWlyZQogICAgICAgICAgICAgICAgICAgICAgICAvL2NhbGwgZm9yIGRlcGVuZGVuY2llcy4KICAgICAgICAgICAgICAgICAgICAgICAgcmVxdWlyZU1vZC5za2lwTWFwID0gb3B0aW9ucy5za2lwTWFwOwoKICAgICAgICAgICAgICAgICAgICAgICAgcmVxdWlyZU1vZC5pbml0KGRlcHMsIGNhbGxiYWNrLCBlcnJiYWNrLCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbmFibGVkOiB0cnVlCiAgICAgICAgICAgICAgICAgICAgICAgIH0pOwoKICAgICAgICAgICAgICAgICAgICAgICAgY2hlY2tMb2FkZWQoKTsKICAgICAgICAgICAgICAgICAgICB9KTsKCiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGxvY2FsUmVxdWlyZTsKICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICBtaXhpbihsb2NhbFJlcXVpcmUsIHsKICAgICAgICAgICAgICAgICAgICBpc0Jyb3dzZXI6IGlzQnJvd3NlciwKCiAgICAgICAgICAgICAgICAgICAgLyoqCiAgICAgICAgICAgICAgICAgICAgICogQ29udmVydHMgYSBtb2R1bGUgbmFtZSArIC5leHRlbnNpb24gaW50byBhbiBVUkwgcGF0aC4KICAgICAgICAgICAgICAgICAgICAgKiAqUmVxdWlyZXMqIHRoZSB1c2Ugb2YgYSBtb2R1bGUgbmFtZS4gSXQgZG9lcyBub3Qgc3VwcG9ydCB1c2luZwogICAgICAgICAgICAgICAgICAgICAqIHBsYWluIFVSTHMgbGlrZSBuYW1lVG9VcmwuCiAgICAgICAgICAgICAgICAgICAgICovCiAgICAgICAgICAgICAgICAgICAgdG9Vcmw6IGZ1bmN0aW9uIChtb2R1bGVOYW1lUGx1c0V4dCkgewogICAgICAgICAgICAgICAgICAgICAgICB2YXIgZXh0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5kZXggPSBtb2R1bGVOYW1lUGx1c0V4dC5sYXN0SW5kZXhPZignLicpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VnbWVudCA9IG1vZHVsZU5hbWVQbHVzRXh0LnNwbGl0KCcvJylbMF0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpc1JlbGF0aXZlID0gc2VnbWVudCA9PT0gJy4nIHx8IHNlZ21lbnQgPT09ICcuLic7CgogICAgICAgICAgICAgICAgICAgICAgICAvL0hhdmUgYSBmaWxlIGV4dGVuc2lvbiBhbGlhcywgYW5kIGl0IGlzIG5vdCB0aGUKICAgICAgICAgICAgICAgICAgICAgICAgLy9kb3RzIGZyb20gYSByZWxhdGl2ZSBwYXRoLgogICAgICAgICAgICAgICAgICAgICAgICBpZiAoaW5kZXggIT09IC0xICYmICghaXNSZWxhdGl2ZSB8fCBpbmRleCA+IDEpKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBleHQgPSBtb2R1bGVOYW1lUGx1c0V4dC5zdWJzdHJpbmcoaW5kZXgsIG1vZHVsZU5hbWVQbHVzRXh0Lmxlbmd0aCk7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtb2R1bGVOYW1lUGx1c0V4dCA9IG1vZHVsZU5hbWVQbHVzRXh0LnN1YnN0cmluZygwLCBpbmRleCk7CiAgICAgICAgICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiBjb250ZXh0Lm5hbWVUb1VybChub3JtYWxpemUobW9kdWxlTmFtZVBsdXNFeHQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlbE1hcCAmJiByZWxNYXAuaWQsIHRydWUpLCBleHQsICB0cnVlKTsKICAgICAgICAgICAgICAgICAgICB9LAoKICAgICAgICAgICAgICAgICAgICBkZWZpbmVkOiBmdW5jdGlvbiAoaWQpIHsKICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGhhc1Byb3AoZGVmaW5lZCwgbWFrZU1vZHVsZU1hcChpZCwgcmVsTWFwLCBmYWxzZSwgdHJ1ZSkuaWQpOwogICAgICAgICAgICAgICAgICAgIH0sCgogICAgICAgICAgICAgICAgICAgIHNwZWNpZmllZDogZnVuY3Rpb24gKGlkKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIGlkID0gbWFrZU1vZHVsZU1hcChpZCwgcmVsTWFwLCBmYWxzZSwgdHJ1ZSkuaWQ7CiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiBoYXNQcm9wKGRlZmluZWQsIGlkKSB8fCBoYXNQcm9wKHJlZ2lzdHJ5LCBpZCk7CiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfSk7CgogICAgICAgICAgICAgICAgLy9Pbmx5IGFsbG93IHVuZGVmIG9uIHRvcCBsZXZlbCByZXF1aXJlIGNhbGxzCiAgICAgICAgICAgICAgICBpZiAoIXJlbE1hcCkgewogICAgICAgICAgICAgICAgICAgIGxvY2FsUmVxdWlyZS51bmRlZiA9IGZ1bmN0aW9uIChpZCkgewogICAgICAgICAgICAgICAgICAgICAgICAvL0JpbmQgYW55IHdhaXRpbmcgZGVmaW5lKCkgY2FsbHMgdG8gdGhpcyBjb250ZXh0LAogICAgICAgICAgICAgICAgICAgICAgICAvL2ZpeCBmb3IgIzQwOAogICAgICAgICAgICAgICAgICAgICAgICB0YWtlR2xvYmFsUXVldWUoKTsKCiAgICAgICAgICAgICAgICAgICAgICAgIHZhciBtYXAgPSBtYWtlTW9kdWxlTWFwKGlkLCByZWxNYXAsIHRydWUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9kID0gZ2V0T3duKHJlZ2lzdHJ5LCBpZCk7CgogICAgICAgICAgICAgICAgICAgICAgICBtb2QudW5kZWZlZCA9IHRydWU7CiAgICAgICAgICAgICAgICAgICAgICAgIHJlbW92ZVNjcmlwdChpZCk7CgogICAgICAgICAgICAgICAgICAgICAgICBkZWxldGUgZGVmaW5lZFtpZF07CiAgICAgICAgICAgICAgICAgICAgICAgIGRlbGV0ZSB1cmxGZXRjaGVkW21hcC51cmxdOwogICAgICAgICAgICAgICAgICAgICAgICBkZWxldGUgdW5kZWZFdmVudHNbaWRdOwoKICAgICAgICAgICAgICAgICAgICAgICAgLy9DbGVhbiBxdWV1ZWQgZGVmaW5lcyB0b28uIEdvIGJhY2t3YXJkcwogICAgICAgICAgICAgICAgICAgICAgICAvL2luIGFycmF5IHNvIHRoYXQgdGhlIHNwbGljZXMgZG8gbm90CiAgICAgICAgICAgICAgICAgICAgICAgIC8vbWVzcyB1cCB0aGUgaXRlcmF0aW9uLgogICAgICAgICAgICAgICAgICAgICAgICBlYWNoUmV2ZXJzZShkZWZRdWV1ZSwgZnVuY3Rpb24oYXJncywgaSkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGFyZ3NbMF0gPT09IGlkKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVmUXVldWUuc3BsaWNlKGksIDEpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9KTsKICAgICAgICAgICAgICAgICAgICAgICAgZGVsZXRlIGNvbnRleHQuZGVmUXVldWVNYXBbaWRdOwoKICAgICAgICAgICAgICAgICAgICAgICAgaWYgKG1vZCkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgLy9Ib2xkIG9uIHRvIGxpc3RlbmVycyBpbiBjYXNlIHRoZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgLy9tb2R1bGUgd2lsbCBiZSBhdHRlbXB0ZWQgdG8gYmUgcmVsb2FkZWQKICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vdXNpbmcgYSBkaWZmZXJlbnQgY29uZmlnLgogICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKG1vZC5ldmVudHMuZGVmaW5lZCkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVuZGVmRXZlbnRzW2lkXSA9IG1vZC5ldmVudHM7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgICAgICAgICAgICAgY2xlYW5SZWdpc3RyeShpZCk7CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICB9OwogICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgIHJldHVybiBsb2NhbFJlcXVpcmU7CiAgICAgICAgICAgIH0sCgogICAgICAgICAgICAvKioKICAgICAgICAgICAgICogQ2FsbGVkIHRvIGVuYWJsZSBhIG1vZHVsZSBpZiBpdCBpcyBzdGlsbCBpbiB0aGUgcmVnaXN0cnkKICAgICAgICAgICAgICogYXdhaXRpbmcgZW5hYmxlbWVudC4gQSBzZWNvbmQgYXJnLCBwYXJlbnQsIHRoZSBwYXJlbnQgbW9kdWxlLAogICAgICAgICAgICAgKiBpcyBwYXNzZWQgaW4gZm9yIGNvbnRleHQsIHdoZW4gdGhpcyBtZXRob2QgaXMgb3ZlcnJpZGRlbiBieQogICAgICAgICAgICAgKiB0aGUgb3B0aW1pemVyLiBOb3Qgc2hvd24gaGVyZSB0byBrZWVwIGNvZGUgY29tcGFjdC4KICAgICAgICAgICAgICovCiAgICAgICAgICAgIGVuYWJsZTogZnVuY3Rpb24gKGRlcE1hcCkgewogICAgICAgICAgICAgICAgdmFyIG1vZCA9IGdldE93bihyZWdpc3RyeSwgZGVwTWFwLmlkKTsKICAgICAgICAgICAgICAgIGlmIChtb2QpIHsKICAgICAgICAgICAgICAgICAgICBnZXRNb2R1bGUoZGVwTWFwKS5lbmFibGUoKTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKCiAgICAgICAgICAgIC8qKgogICAgICAgICAgICAgKiBJbnRlcm5hbCBtZXRob2QgdXNlZCBieSBlbnZpcm9ubWVudCBhZGFwdGVycyB0byBjb21wbGV0ZSBhIGxvYWQgZXZlbnQuCiAgICAgICAgICAgICAqIEEgbG9hZCBldmVudCBjb3VsZCBiZSBhIHNjcmlwdCBsb2FkIG9yIGp1c3QgYSBsb2FkIHBhc3MgZnJvbSBhIHN5bmNocm9ub3VzCiAgICAgICAgICAgICAqIGxvYWQgY2FsbC4KICAgICAgICAgICAgICogQHBhcmFtIHtTdHJpbmd9IG1vZHVsZU5hbWUgdGhlIG5hbWUgb2YgdGhlIG1vZHVsZSB0byBwb3RlbnRpYWxseSBjb21wbGV0ZS4KICAgICAgICAgICAgICovCiAgICAgICAgICAgIGNvbXBsZXRlTG9hZDogZnVuY3Rpb24gKG1vZHVsZU5hbWUpIHsKICAgICAgICAgICAgICAgIHZhciBmb3VuZCwgYXJncywgbW9kLAogICAgICAgICAgICAgICAgICAgIHNoaW0gPSBnZXRPd24oY29uZmlnLnNoaW0sIG1vZHVsZU5hbWUpIHx8IHt9LAogICAgICAgICAgICAgICAgICAgIHNoRXhwb3J0cyA9IHNoaW0uZXhwb3J0czsKCiAgICAgICAgICAgICAgICB0YWtlR2xvYmFsUXVldWUoKTsKCiAgICAgICAgICAgICAgICB3aGlsZSAoZGVmUXVldWUubGVuZ3RoKSB7CiAgICAgICAgICAgICAgICAgICAgYXJncyA9IGRlZlF1ZXVlLnNoaWZ0KCk7CiAgICAgICAgICAgICAgICAgICAgaWYgKGFyZ3NbMF0gPT09IG51bGwpIHsKICAgICAgICAgICAgICAgICAgICAgICAgYXJnc1swXSA9IG1vZHVsZU5hbWU7CiAgICAgICAgICAgICAgICAgICAgICAgIC8vSWYgYWxyZWFkeSBmb3VuZCBhbiBhbm9ueW1vdXMgbW9kdWxlIGFuZCBib3VuZCBpdAogICAgICAgICAgICAgICAgICAgICAgICAvL3RvIHRoaXMgbmFtZSwgdGhlbiB0aGlzIGlzIHNvbWUgb3RoZXIgYW5vbiBtb2R1bGUKICAgICAgICAgICAgICAgICAgICAgICAgLy93YWl0aW5nIGZvciBpdHMgY29tcGxldGVMb2FkIHRvIGZpcmUuCiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChmb3VuZCkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgZm91bmQgPSB0cnVlOwogICAgICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAoYXJnc1swXSA9PT0gbW9kdWxlTmFtZSkgewogICAgICAgICAgICAgICAgICAgICAgICAvL0ZvdW5kIG1hdGNoaW5nIGRlZmluZSBjYWxsIGZvciB0aGlzIHNjcmlwdCEKICAgICAgICAgICAgICAgICAgICAgICAgZm91bmQgPSB0cnVlOwogICAgICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICAgICAgY2FsbEdldE1vZHVsZShhcmdzKTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIGNvbnRleHQuZGVmUXVldWVNYXAgPSB7fTsKCiAgICAgICAgICAgICAgICAvL0RvIHRoaXMgYWZ0ZXIgdGhlIGN5Y2xlIG9mIGNhbGxHZXRNb2R1bGUgaW4gY2FzZSB0aGUgcmVzdWx0CiAgICAgICAgICAgICAgICAvL29mIHRob3NlIGNhbGxzL2luaXQgY2FsbHMgY2hhbmdlcyB0aGUgcmVnaXN0cnkuCiAgICAgICAgICAgICAgICBtb2QgPSBnZXRPd24ocmVnaXN0cnksIG1vZHVsZU5hbWUpOwoKICAgICAgICAgICAgICAgIGlmICghZm91bmQgJiYgIWhhc1Byb3AoZGVmaW5lZCwgbW9kdWxlTmFtZSkgJiYgbW9kICYmICFtb2QuaW5pdGVkKSB7CiAgICAgICAgICAgICAgICAgICAgaWYgKGNvbmZpZy5lbmZvcmNlRGVmaW5lICYmICghc2hFeHBvcnRzIHx8ICFnZXRHbG9iYWwoc2hFeHBvcnRzKSkpIHsKICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGhhc1BhdGhGYWxsYmFjayhtb2R1bGVOYW1lKSkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuOwogICAgICAgICAgICAgICAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIG9uRXJyb3IobWFrZUVycm9yKCdub2RlZmluZScsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdObyBkZWZpbmUgY2FsbCBmb3IgJyArIG1vZHVsZU5hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG51bGwsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFttb2R1bGVOYW1lXSkpOwogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgICAgICAgICAgICAgLy9BIHNjcmlwdCB0aGF0IGRvZXMgbm90IGNhbGwgZGVmaW5lKCksIHNvIGp1c3Qgc2ltdWxhdGUKICAgICAgICAgICAgICAgICAgICAgICAgLy90aGUgY2FsbCBmb3IgaXQuCiAgICAgICAgICAgICAgICAgICAgICAgIGNhbGxHZXRNb2R1bGUoW21vZHVsZU5hbWUsIChzaGltLmRlcHMgfHwgW10pLCBzaGltLmV4cG9ydHNGbl0pOwogICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICBjaGVja0xvYWRlZCgpOwogICAgICAgICAgICB9LAoKICAgICAgICAgICAgLyoqCiAgICAgICAgICAgICAqIENvbnZlcnRzIGEgbW9kdWxlIG5hbWUgdG8gYSBmaWxlIHBhdGguIFN1cHBvcnRzIGNhc2VzIHdoZXJlCiAgICAgICAgICAgICAqIG1vZHVsZU5hbWUgbWF5IGFjdHVhbGx5IGJlIGp1c3QgYW4gVVJMLgogICAgICAgICAgICAgKiBOb3RlIHRoYXQgaXQgKipkb2VzIG5vdCoqIGNhbGwgbm9ybWFsaXplIG9uIHRoZSBtb2R1bGVOYW1lLAogICAgICAgICAgICAgKiBpdCBpcyBhc3N1bWVkIHRvIGhhdmUgYWxyZWFkeSBiZWVuIG5vcm1hbGl6ZWQuIFRoaXMgaXMgYW4KICAgICAgICAgICAgICogaW50ZXJuYWwgQVBJLCBub3QgYSBwdWJsaWMgb25lLiBVc2UgdG9VcmwgZm9yIHRoZSBwdWJsaWMgQVBJLgogICAgICAgICAgICAgKi8KICAgICAgICAgICAgbmFtZVRvVXJsOiBmdW5jdGlvbiAobW9kdWxlTmFtZSwgZXh0LCBza2lwRXh0KSB7CiAgICAgICAgICAgICAgICB2YXIgcGF0aHMsIHN5bXMsIGksIHBhcmVudE1vZHVsZSwgdXJsLAogICAgICAgICAgICAgICAgICAgIHBhcmVudFBhdGgsIGJ1bmRsZUlkLAogICAgICAgICAgICAgICAgICAgIHBrZ01haW4gPSBnZXRPd24oY29uZmlnLnBrZ3MsIG1vZHVsZU5hbWUpOwoKICAgICAgICAgICAgICAgIGlmIChwa2dNYWluKSB7CiAgICAgICAgICAgICAgICAgICAgbW9kdWxlTmFtZSA9IHBrZ01haW47CiAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgYnVuZGxlSWQgPSBnZXRPd24oYnVuZGxlc01hcCwgbW9kdWxlTmFtZSk7CgogICAgICAgICAgICAgICAgaWYgKGJ1bmRsZUlkKSB7CiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGNvbnRleHQubmFtZVRvVXJsKGJ1bmRsZUlkLCBleHQsIHNraXBFeHQpOwogICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgIC8vSWYgYSBjb2xvbiBpcyBpbiB0aGUgVVJMLCBpdCBpbmRpY2F0ZXMgYSBwcm90b2NvbCBpcyB1c2VkIGFuZCBpdCBpcyBqdXN0CiAgICAgICAgICAgICAgICAvL2FuIFVSTCB0byBhIGZpbGUsIG9yIGlmIGl0IHN0YXJ0cyB3aXRoIGEgc2xhc2gsIGNvbnRhaW5zIGEgcXVlcnkgYXJnIChpLmUuID8pCiAgICAgICAgICAgICAgICAvL29yIGVuZHMgd2l0aCAuanMsIHRoZW4gYXNzdW1lIHRoZSB1c2VyIG1lYW50IHRvIHVzZSBhbiB1cmwgYW5kIG5vdCBhIG1vZHVsZSBpZC4KICAgICAgICAgICAgICAgIC8vVGhlIHNsYXNoIGlzIGltcG9ydGFudCBmb3IgcHJvdG9jb2wtbGVzcyBVUkxzIGFzIHdlbGwgYXMgZnVsbCBwYXRocy4KICAgICAgICAgICAgICAgIGlmIChyZXEuanNFeHRSZWdFeHAudGVzdChtb2R1bGVOYW1lKSkgewogICAgICAgICAgICAgICAgICAgIC8vSnVzdCBhIHBsYWluIHBhdGgsIG5vdCBtb2R1bGUgbmFtZSBsb29rdXAsIHNvIGp1c3QgcmV0dXJuIGl0LgogICAgICAgICAgICAgICAgICAgIC8vQWRkIGV4dGVuc2lvbiBpZiBpdCBpcyBpbmNsdWRlZC4gVGhpcyBpcyBhIGJpdCB3b25reSwgb25seSBub24tLmpzIHRoaW5ncyBwYXNzCiAgICAgICAgICAgICAgICAgICAgLy9hbiBleHRlbnNpb24sIHRoaXMgbWV0aG9kIHByb2JhYmx5IG5lZWRzIHRvIGJlIHJld29ya2VkLgogICAgICAgICAgICAgICAgICAgIHVybCA9IG1vZHVsZU5hbWUgKyAoZXh0IHx8ICcnKTsKICAgICAgICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgICAgICAgICAgLy9BIG1vZHVsZSB0aGF0IG5lZWRzIHRvIGJlIGNvbnZlcnRlZCB0byBhIHBhdGguCiAgICAgICAgICAgICAgICAgICAgcGF0aHMgPSBjb25maWcucGF0aHM7CgogICAgICAgICAgICAgICAgICAgIHN5bXMgPSBtb2R1bGVOYW1lLnNwbGl0KCcvJyk7CiAgICAgICAgICAgICAgICAgICAgLy9Gb3IgZWFjaCBtb2R1bGUgbmFtZSBzZWdtZW50LCBzZWUgaWYgdGhlcmUgaXMgYSBwYXRoCiAgICAgICAgICAgICAgICAgICAgLy9yZWdpc3RlcmVkIGZvciBpdC4gU3RhcnQgd2l0aCBtb3N0IHNwZWNpZmljIG5hbWUKICAgICAgICAgICAgICAgICAgICAvL2FuZCB3b3JrIHVwIGZyb20gaXQuCiAgICAgICAgICAgICAgICAgICAgZm9yIChpID0gc3ltcy5sZW5ndGg7IGkgPiAwOyBpIC09IDEpIHsKICAgICAgICAgICAgICAgICAgICAgICAgcGFyZW50TW9kdWxlID0gc3ltcy5zbGljZSgwLCBpKS5qb2luKCcvJyk7CgogICAgICAgICAgICAgICAgICAgICAgICBwYXJlbnRQYXRoID0gZ2V0T3duKHBhdGhzLCBwYXJlbnRNb2R1bGUpOwogICAgICAgICAgICAgICAgICAgICAgICBpZiAocGFyZW50UGF0aCkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgLy9JZiBhbiBhcnJheSwgaXQgbWVhbnMgdGhlcmUgYXJlIGEgZmV3IGNob2ljZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvL0Nob29zZSB0aGUgb25lIHRoYXQgaXMgZGVzaXJlZAogICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGlzQXJyYXkocGFyZW50UGF0aCkpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXJlbnRQYXRoID0gcGFyZW50UGF0aFswXTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN5bXMuc3BsaWNlKDAsIGksIHBhcmVudFBhdGgpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgICAgIC8vSm9pbiB0aGUgcGF0aCBwYXJ0cyB0b2dldGhlciwgdGhlbiBmaWd1cmUgb3V0IGlmIGJhc2VVcmwgaXMgbmVlZGVkLgogICAgICAgICAgICAgICAgICAgIHVybCA9IHN5bXMuam9pbignLycpOwogICAgICAgICAgICAgICAgICAgIHVybCArPSAoZXh0IHx8ICgvXmRhdGFcOnxcPy8udGVzdCh1cmwpIHx8IHNraXBFeHQgPyAnJyA6ICcuanMnKSk7CiAgICAgICAgICAgICAgICAgICAgdXJsID0gKHVybC5jaGFyQXQoMCkgPT09ICcvJyB8fCB1cmwubWF0Y2goL15bXHdcK1wuXC1dKzovKSA/ICcnIDogY29uZmlnLmJhc2VVcmwpICsgdXJsOwogICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgIHJldHVybiBjb25maWcudXJsQXJncyA/IHVybCArCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoKHVybC5pbmRleE9mKCc/JykgPT09IC0xID8gJz8nIDogJyYnKSArCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29uZmlnLnVybEFyZ3MpIDogdXJsOwogICAgICAgICAgICB9LAoKICAgICAgICAgICAgLy9EZWxlZ2F0ZXMgdG8gcmVxLmxvYWQuIEJyb2tlbiBvdXQgYXMgYSBzZXBhcmF0ZSBmdW5jdGlvbiB0bwogICAgICAgICAgICAvL2FsbG93IG92ZXJyaWRpbmcgaW4gdGhlIG9wdGltaXplci4KICAgICAgICAgICAgbG9hZDogZnVuY3Rpb24gKGlkLCB1cmwpIHsKICAgICAgICAgICAgICAgIHJlcS5sb2FkKGNvbnRleHQsIGlkLCB1cmwpOwogICAgICAgICAgICB9LAoKICAgICAgICAgICAgLyoqCiAgICAgICAgICAgICAqIEV4ZWN1dGVzIGEgbW9kdWxlIGNhbGxiYWNrIGZ1bmN0aW9uLiBCcm9rZW4gb3V0IGFzIGEgc2VwYXJhdGUgZnVuY3Rpb24KICAgICAgICAgICAgICogc29sZWx5IHRvIGFsbG93IHRoZSBidWlsZCBzeXN0ZW0gdG8gc2VxdWVuY2UgdGhlIGZpbGVzIGluIHRoZSBidWlsdAogICAgICAgICAgICAgKiBsYXllciBpbiB0aGUgcmlnaHQgc2VxdWVuY2UuCiAgICAgICAgICAgICAqCiAgICAgICAgICAgICAqIEBwcml2YXRlCiAgICAgICAgICAgICAqLwogICAgICAgICAgICBleGVjQ2I6IGZ1bmN0aW9uIChuYW1lLCBjYWxsYmFjaywgYXJncywgZXhwb3J0cykgewogICAgICAgICAgICAgICAgcmV0dXJuIGNhbGxiYWNrLmFwcGx5KGV4cG9ydHMsIGFyZ3MpOwogICAgICAgICAgICB9LAoKICAgICAgICAgICAgLyoqCiAgICAgICAgICAgICAqIGNhbGxiYWNrIGZvciBzY3JpcHQgbG9hZHMsIHVzZWQgdG8gY2hlY2sgc3RhdHVzIG9mIGxvYWRpbmcuCiAgICAgICAgICAgICAqCiAgICAgICAgICAgICAqIEBwYXJhbSB7RXZlbnR9IGV2dCB0aGUgZXZlbnQgZnJvbSB0aGUgYnJvd3NlciBmb3IgdGhlIHNjcmlwdAogICAgICAgICAgICAgKiB0aGF0IHdhcyBsb2FkZWQuCiAgICAgICAgICAgICAqLwogICAgICAgICAgICBvblNjcmlwdExvYWQ6IGZ1bmN0aW9uIChldnQpIHsKICAgICAgICAgICAgICAgIC8vVXNpbmcgY3VycmVudFRhcmdldCBpbnN0ZWFkIG9mIHRhcmdldCBmb3IgRmlyZWZveCAyLjAncyBzYWtlLiBOb3QKICAgICAgICAgICAgICAgIC8vYWxsIG9sZCBicm93c2VycyB3aWxsIGJlIHN1cHBvcnRlZCwgYnV0IHRoaXMgb25lIHdhcyBlYXN5IGVub3VnaAogICAgICAgICAgICAgICAgLy90byBzdXBwb3J0IGFuZCBzdGlsbCBtYWtlcyBzZW5zZS4KICAgICAgICAgICAgICAgIGlmIChldnQudHlwZSA9PT0gJ2xvYWQnIHx8CiAgICAgICAgICAgICAgICAgICAgICAgIChyZWFkeVJlZ0V4cC50ZXN0KChldnQuY3VycmVudFRhcmdldCB8fCBldnQuc3JjRWxlbWVudCkucmVhZHlTdGF0ZSkpKSB7CiAgICAgICAgICAgICAgICAgICAgLy9SZXNldCBpbnRlcmFjdGl2ZSBzY3JpcHQgc28gYSBzY3JpcHQgbm9kZSBpcyBub3QgaGVsZCBvbnRvIGZvcgogICAgICAgICAgICAgICAgICAgIC8vdG8gbG9uZy4KICAgICAgICAgICAgICAgICAgICBpbnRlcmFjdGl2ZVNjcmlwdCA9IG51bGw7CgogICAgICAgICAgICAgICAgICAgIC8vUHVsbCBvdXQgdGhlIG5hbWUgb2YgdGhlIG1vZHVsZSBhbmQgdGhlIGNvbnRleHQuCiAgICAgICAgICAgICAgICAgICAgdmFyIGRhdGEgPSBnZXRTY3JpcHREYXRhKGV2dCk7CiAgICAgICAgICAgICAgICAgICAgY29udGV4dC5jb21wbGV0ZUxvYWQoZGF0YS5pZCk7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCgogICAgICAgICAgICAvKioKICAgICAgICAgICAgICogQ2FsbGJhY2sgZm9yIHNjcmlwdCBlcnJvcnMuCiAgICAgICAgICAgICAqLwogICAgICAgICAgICBvblNjcmlwdEVycm9yOiBmdW5jdGlvbiAoZXZ0KSB7CiAgICAgICAgICAgICAgICB2YXIgZGF0YSA9IGdldFNjcmlwdERhdGEoZXZ0KTsKICAgICAgICAgICAgICAgIGlmICghaGFzUGF0aEZhbGxiYWNrKGRhdGEuaWQpKSB7CiAgICAgICAgICAgICAgICAgICAgdmFyIHBhcmVudHMgPSBbXTsKICAgICAgICAgICAgICAgICAgICBlYWNoUHJvcChyZWdpc3RyeSwgZnVuY3Rpb24odmFsdWUsIGtleSkgewogICAgICAgICAgICAgICAgICAgICAgICBpZiAoa2V5LmluZGV4T2YoJ19AcicpICE9PSAwKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBlYWNoKHZhbHVlLmRlcE1hcHMsIGZ1bmN0aW9uKGRlcE1hcCkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIChkZXBNYXAuaWQgPT09IGRhdGEuaWQpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFyZW50cy5wdXNoKGtleSk7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiB0cnVlOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgfSk7CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICB9KTsKICAgICAgICAgICAgICAgICAgICByZXR1cm4gb25FcnJvcihtYWtlRXJyb3IoJ3NjcmlwdGVycm9yJywgJ1NjcmlwdCBlcnJvciBmb3IgIicgKyBkYXRhLmlkICsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKHBhcmVudHMubGVuZ3RoID8KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJyIsIG5lZWRlZCBieTogJyArIHBhcmVudHMuam9pbignLCAnKSA6CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICciJyksIGV2dCwgW2RhdGEuaWRdKSk7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICB9OwoKICAgICAgICBjb250ZXh0LnJlcXVpcmUgPSBjb250ZXh0Lm1ha2VSZXF1aXJlKCk7CiAgICAgICAgcmV0dXJuIGNvbnRleHQ7CiAgICB9CgogICAgLyoqCiAgICAgKiBNYWluIGVudHJ5IHBvaW50LgogICAgICoKICAgICAqIElmIHRoZSBvbmx5IGFyZ3VtZW50IHRvIHJlcXVpcmUgaXMgYSBzdHJpbmcsIHRoZW4gdGhlIG1vZHVsZSB0aGF0CiAgICAgKiBpcyByZXByZXNlbnRlZCBieSB0aGF0IHN0cmluZyBpcyBmZXRjaGVkIGZvciB0aGUgYXBwcm9wcmlhdGUgY29udGV4dC4KICAgICAqCiAgICAgKiBJZiB0aGUgZmlyc3QgYXJndW1lbnQgaXMgYW4gYXJyYXksIHRoZW4gaXQgd2lsbCBiZSB0cmVhdGVkIGFzIGFuIGFycmF5CiAgICAgKiBvZiBkZXBlbmRlbmN5IHN0cmluZyBuYW1lcyB0byBmZXRjaC4gQW4gb3B0aW9uYWwgZnVuY3Rpb24gY2FsbGJhY2sgY2FuCiAgICAgKiBiZSBzcGVjaWZpZWQgdG8gZXhlY3V0ZSB3aGVuIGFsbCBvZiB0aG9zZSBkZXBlbmRlbmNpZXMgYXJlIGF2YWlsYWJsZS4KICAgICAqCiAgICAgKiBNYWtlIGEgbG9jYWwgcmVxIHZhcmlhYmxlIHRvIGhlbHAgQ2FqYSBjb21wbGlhbmNlIChpdCBhc3N1bWVzIHRoaW5ncwogICAgICogb24gYSByZXF1aXJlIHRoYXQgYXJlIG5vdCBzdGFuZGFyZGl6ZWQpLCBhbmQgdG8gZ2l2ZSBhIHNob3J0CiAgICAgKiBuYW1lIGZvciBtaW5pZmljYXRpb24vbG9jYWwgc2NvcGUgdXNlLgogICAgICovCiAgICByZXEgPSByZXF1aXJlanMgPSBmdW5jdGlvbiAoZGVwcywgY2FsbGJhY2ssIGVycmJhY2ssIG9wdGlvbmFsKSB7CgogICAgICAgIC8vRmluZCB0aGUgcmlnaHQgY29udGV4dCwgdXNlIGRlZmF1bHQKICAgICAgICB2YXIgY29udGV4dCwgY29uZmlnLAogICAgICAgICAgICBjb250ZXh0TmFtZSA9IGRlZkNvbnRleHROYW1lOwoKICAgICAgICAvLyBEZXRlcm1pbmUgaWYgaGF2ZSBjb25maWcgb2JqZWN0IGluIHRoZSBjYWxsLgogICAgICAgIGlmICghaXNBcnJheShkZXBzKSAmJiB0eXBlb2YgZGVwcyAhPT0gJ3N0cmluZycpIHsKICAgICAgICAgICAgLy8gZGVwcyBpcyBhIGNvbmZpZyBvYmplY3QKICAgICAgICAgICAgY29uZmlnID0gZGVwczsKICAgICAgICAgICAgaWYgKGlzQXJyYXkoY2FsbGJhY2spKSB7CiAgICAgICAgICAgICAgICAvLyBBZGp1c3QgYXJncyBpZiB0aGVyZSBhcmUgZGVwZW5kZW5jaWVzCiAgICAgICAgICAgICAgICBkZXBzID0gY2FsbGJhY2s7CiAgICAgICAgICAgICAgICBjYWxsYmFjayA9IGVycmJhY2s7CiAgICAgICAgICAgICAgICBlcnJiYWNrID0gb3B0aW9uYWw7CiAgICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgICAgICBkZXBzID0gW107CiAgICAgICAgICAgIH0KICAgICAgICB9CgogICAgICAgIGlmIChjb25maWcgJiYgY29uZmlnLmNvbnRleHQpIHsKICAgICAgICAgICAgY29udGV4dE5hbWUgPSBjb25maWcuY29udGV4dDsKICAgICAgICB9CgogICAgICAgIGNvbnRleHQgPSBnZXRPd24oY29udGV4dHMsIGNvbnRleHROYW1lKTsKICAgICAgICBpZiAoIWNvbnRleHQpIHsKICAgICAgICAgICAgY29udGV4dCA9IGNvbnRleHRzW2NvbnRleHROYW1lXSA9IHJlcS5zLm5ld0NvbnRleHQoY29udGV4dE5hbWUpOwogICAgICAgIH0KCiAgICAgICAgaWYgKGNvbmZpZykgewogICAgICAgICAgICBjb250ZXh0LmNvbmZpZ3VyZShjb25maWcpOwogICAgICAgIH0KCiAgICAgICAgcmV0dXJuIGNvbnRleHQucmVxdWlyZShkZXBzLCBjYWxsYmFjaywgZXJyYmFjayk7CiAgICB9OwoKICAgIC8qKgogICAgICogU3VwcG9ydCByZXF1aXJlLmNvbmZpZygpIHRvIG1ha2UgaXQgZWFzaWVyIHRvIGNvb3BlcmF0ZSB3aXRoIG90aGVyCiAgICAgKiBBTUQgbG9hZGVycyBvbiBnbG9iYWxseSBhZ3JlZWQgbmFtZXMuCiAgICAgKi8KICAgIHJlcS5jb25maWcgPSBmdW5jdGlvbiAoY29uZmlnKSB7CiAgICAgICAgcmV0dXJuIHJlcShjb25maWcpOwogICAgfTsKCiAgICAvKioKICAgICAqIEV4ZWN1dGUgc29tZXRoaW5nIGFmdGVyIHRoZSBjdXJyZW50IHRpY2sKICAgICAqIG9mIHRoZSBldmVudCBsb29wLiBPdmVycmlkZSBmb3Igb3RoZXIgZW52cwogICAgICogdGhhdCBoYXZlIGEgYmV0dGVyIHNvbHV0aW9uIHRoYW4gc2V0VGltZW91dC4KICAgICAqIEBwYXJhbSAge0Z1bmN0aW9ufSBmbiBmdW5jdGlvbiB0byBleGVjdXRlIGxhdGVyLgogICAgICovCiAgICByZXEubmV4dFRpY2sgPSB0eXBlb2Ygc2V0VGltZW91dCAhPT0gJ3VuZGVmaW5lZCcgPyBmdW5jdGlvbiAoZm4pIHsKICAgICAgICBzZXRUaW1lb3V0KGZuLCA0KTsKICAgIH0gOiBmdW5jdGlvbiAoZm4pIHsgZm4oKTsgfTsKCiAgICAvKioKICAgICAqIEV4cG9ydCByZXF1aXJlIGFzIGEgZ2xvYmFsLCBidXQgb25seSBpZiBpdCBkb2VzIG5vdCBhbHJlYWR5IGV4aXN0LgogICAgICovCiAgICBpZiAoIXJlcXVpcmUpIHsKICAgICAgICByZXF1aXJlID0gcmVxOwogICAgfQoKICAgIHJlcS52ZXJzaW9uID0gdmVyc2lvbjsKCiAgICAvL1VzZWQgdG8gZmlsdGVyIG91dCBkZXBlbmRlbmNpZXMgdGhhdCBhcmUgYWxyZWFkeSBwYXRocy4KICAgIHJlcS5qc0V4dFJlZ0V4cCA9IC9eXC98OnxcP3xcLmpzJC87CiAgICByZXEuaXNCcm93c2VyID0gaXNCcm93c2VyOwogICAgcyA9IHJlcS5zID0gewogICAgICAgIGNvbnRleHRzOiBjb250ZXh0cywKICAgICAgICBuZXdDb250ZXh0OiBuZXdDb250ZXh0CiAgICB9OwoKICAgIC8vQ3JlYXRlIGRlZmF1bHQgY29udGV4dC4KICAgIHJlcSh7fSk7CgogICAgLy9FeHBvcnRzIHNvbWUgY29udGV4dC1zZW5zaXRpdmUgbWV0aG9kcyBvbiBnbG9iYWwgcmVxdWlyZS4KICAgIGVhY2goWwogICAgICAgICd0b1VybCcsCiAgICAgICAgJ3VuZGVmJywKICAgICAgICAnZGVmaW5lZCcsCiAgICAgICAgJ3NwZWNpZmllZCcKICAgIF0sIGZ1bmN0aW9uIChwcm9wKSB7CiAgICAgICAgLy9SZWZlcmVuY2UgZnJvbSBjb250ZXh0cyBpbnN0ZWFkIG9mIGVhcmx5IGJpbmRpbmcgdG8gZGVmYXVsdCBjb250ZXh0LAogICAgICAgIC8vc28gdGhhdCBkdXJpbmcgYnVpbGRzLCB0aGUgbGF0ZXN0IGluc3RhbmNlIG9mIHRoZSBkZWZhdWx0IGNvbnRleHQKICAgICAgICAvL3dpdGggaXRzIGNvbmZpZyBnZXRzIHVzZWQuCiAgICAgICAgcmVxW3Byb3BdID0gZnVuY3Rpb24gKCkgewogICAgICAgICAgICB2YXIgY3R4ID0gY29udGV4dHNbZGVmQ29udGV4dE5hbWVdOwogICAgICAgICAgICByZXR1cm4gY3R4LnJlcXVpcmVbcHJvcF0uYXBwbHkoY3R4LCBhcmd1bWVudHMpOwogICAgICAgIH07CiAgICB9KTsKCiAgICBpZiAoaXNCcm93c2VyKSB7CiAgICAgICAgaGVhZCA9IHMuaGVhZCA9IGRvY3VtZW50LmdldEVsZW1lbnRzQnlUYWdOYW1lKCdoZWFkJylbMF07CiAgICAgICAgLy9JZiBCQVNFIHRhZyBpcyBpbiBwbGF5LCB1c2luZyBhcHBlbmRDaGlsZCBpcyBhIHByb2JsZW0gZm9yIElFNi4KICAgICAgICAvL1doZW4gdGhhdCBicm93c2VyIGRpZXMsIHRoaXMgY2FuIGJlIHJlbW92ZWQuIERldGFpbHMgaW4gdGhpcyBqUXVlcnkgYnVnOgogICAgICAgIC8vaHR0cDovL2Rldi5qcXVlcnkuY29tL3RpY2tldC8yNzA5CiAgICAgICAgYmFzZUVsZW1lbnQgPSBkb2N1bWVudC5nZXRFbGVtZW50c0J5VGFnTmFtZSgnYmFzZScpWzBdOwogICAgICAgIGlmIChiYXNlRWxlbWVudCkgewogICAgICAgICAgICBoZWFkID0gcy5oZWFkID0gYmFzZUVsZW1lbnQucGFyZW50Tm9kZTsKICAgICAgICB9CiAgICB9CgogICAgLyoqCiAgICAgKiBBbnkgZXJyb3JzIHRoYXQgcmVxdWlyZSBleHBsaWNpdGx5IGdlbmVyYXRlcyB3aWxsIGJlIHBhc3NlZCB0byB0aGlzCiAgICAgKiBmdW5jdGlvbi4gSW50ZXJjZXB0L292ZXJyaWRlIGl0IGlmIHlvdSB3YW50IGN1c3RvbSBlcnJvciBoYW5kbGluZy4KICAgICAqIEBwYXJhbSB7RXJyb3J9IGVyciB0aGUgZXJyb3Igb2JqZWN0LgogICAgICovCiAgICByZXEub25FcnJvciA9IGRlZmF1bHRPbkVycm9yOwoKICAgIC8qKgogICAgICogQ3JlYXRlcyB0aGUgbm9kZSBmb3IgdGhlIGxvYWQgY29tbWFuZC4gT25seSB1c2VkIGluIGJyb3dzZXIgZW52cy4KICAgICAqLwogICAgcmVxLmNyZWF0ZU5vZGUgPSBmdW5jdGlvbiAoY29uZmlnLCBtb2R1bGVOYW1lLCB1cmwpIHsKICAgICAgICB2YXIgbm9kZSA9IGNvbmZpZy54aHRtbCA/CiAgICAgICAgICAgICAgICBkb2N1bWVudC5jcmVhdGVFbGVtZW50TlMoJ2h0dHA6Ly93d3cudzMub3JnLzE5OTkveGh0bWwnLCAnaHRtbDpzY3JpcHQnKSA6CiAgICAgICAgICAgICAgICBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzY3JpcHQnKTsKICAgICAgICBub2RlLnR5cGUgPSBjb25maWcuc2NyaXB0VHlwZSB8fCAndGV4dC9qYXZhc2NyaXB0JzsKICAgICAgICBub2RlLmNoYXJzZXQgPSAndXRmLTgnOwogICAgICAgIG5vZGUuYXN5bmMgPSB0cnVlOwogICAgICAgIHJldHVybiBub2RlOwogICAgfTsKCiAgICAvKioKICAgICAqIERvZXMgdGhlIHJlcXVlc3QgdG8gbG9hZCBhIG1vZHVsZSBmb3IgdGhlIGJyb3dzZXIgY2FzZS4KICAgICAqIE1ha2UgdGhpcyBhIHNlcGFyYXRlIGZ1bmN0aW9uIHRvIGFsbG93IG90aGVyIGVudmlyb25tZW50cwogICAgICogdG8gb3ZlcnJpZGUgaXQuCiAgICAgKgogICAgICogQHBhcmFtIHtPYmplY3R9IGNvbnRleHQgdGhlIHJlcXVpcmUgY29udGV4dCB0byBmaW5kIHN0YXRlLgogICAgICogQHBhcmFtIHtTdHJpbmd9IG1vZHVsZU5hbWUgdGhlIG5hbWUgb2YgdGhlIG1vZHVsZS4KICAgICAqIEBwYXJhbSB7T2JqZWN0fSB1cmwgdGhlIFVSTCB0byB0aGUgbW9kdWxlLgogICAgICovCiAgICByZXEubG9hZCA9IGZ1bmN0aW9uIChjb250ZXh0LCBtb2R1bGVOYW1lLCB1cmwpIHsKICAgICAgICB2YXIgY29uZmlnID0gKGNvbnRleHQgJiYgY29udGV4dC5jb25maWcpIHx8IHt9LAogICAgICAgICAgICBub2RlOwogICAgICAgIGlmIChpc0Jyb3dzZXIpIHsKICAgICAgICAgICAgLy9JbiB0aGUgYnJvd3NlciBzbyB1c2UgYSBzY3JpcHQgdGFnCiAgICAgICAgICAgIG5vZGUgPSByZXEuY3JlYXRlTm9kZShjb25maWcsIG1vZHVsZU5hbWUsIHVybCk7CiAgICAgICAgICAgIGlmIChjb25maWcub25Ob2RlQ3JlYXRlZCkgewogICAgICAgICAgICAgICAgY29uZmlnLm9uTm9kZUNyZWF0ZWQobm9kZSwgY29uZmlnLCBtb2R1bGVOYW1lLCB1cmwpOwogICAgICAgICAgICB9CgogICAgICAgICAgICBub2RlLnNldEF0dHJpYnV0ZSgnZGF0YS1yZXF1aXJlY29udGV4dCcsIGNvbnRleHQuY29udGV4dE5hbWUpOwogICAgICAgICAgICBub2RlLnNldEF0dHJpYnV0ZSgnZGF0YS1yZXF1aXJlbW9kdWxlJywgbW9kdWxlTmFtZSk7CgogICAgICAgICAgICAvL1NldCB1cCBsb2FkIGxpc3RlbmVyLiBUZXN0IGF0dGFjaEV2ZW50IGZpcnN0IGJlY2F1c2UgSUU5IGhhcwogICAgICAgICAgICAvL2Egc3VidGxlIGlzc3VlIGluIGl0cyBhZGRFdmVudExpc3RlbmVyIGFuZCBzY3JpcHQgb25sb2FkIGZpcmluZ3MKICAgICAgICAgICAgLy90aGF0IGRvIG5vdCBtYXRjaCB0aGUgYmVoYXZpb3Igb2YgYWxsIG90aGVyIGJyb3dzZXJzIHdpdGgKICAgICAgICAgICAgLy9hZGRFdmVudExpc3RlbmVyIHN1cHBvcnQsIHdoaWNoIGZpcmUgdGhlIG9ubG9hZCBldmVudCBmb3IgYQogICAgICAgICAgICAvL3NjcmlwdCByaWdodCBhZnRlciB0aGUgc2NyaXB0IGV4ZWN1dGlvbi4gU2VlOgogICAgICAgICAgICAvL2h0dHBzOi8vY29ubmVjdC5taWNyb3NvZnQuY29tL0lFL2ZlZWRiYWNrL2RldGFpbHMvNjQ4MDU3L3NjcmlwdC1vbmxvYWQtZXZlbnQtaXMtbm90LWZpcmVkLWltbWVkaWF0ZWx5LWFmdGVyLXNjcmlwdC1leGVjdXRpb24KICAgICAgICAgICAgLy9VTkZPUlRVTkFURUxZIE9wZXJhIGltcGxlbWVudHMgYXR0YWNoRXZlbnQgYnV0IGRvZXMgbm90IGZvbGxvdyB0aGUgc2NyaXB0CiAgICAgICAgICAgIC8vc2NyaXB0IGV4ZWN1dGlvbiBtb2RlLgogICAgICAgICAgICBpZiAobm9kZS5hdHRhY2hFdmVudCAmJgogICAgICAgICAgICAgICAgICAgIC8vQ2hlY2sgaWYgbm9kZS5hdHRhY2hFdmVudCBpcyBhcnRpZmljaWFsbHkgYWRkZWQgYnkgY3VzdG9tIHNjcmlwdCBvcgogICAgICAgICAgICAgICAgICAgIC8vbmF0aXZlbHkgc3VwcG9ydGVkIGJ5IGJyb3dzZXIKICAgICAgICAgICAgICAgICAgICAvL3JlYWQgaHR0cHM6Ly9naXRodWIuY29tL2pyYnVya2UvcmVxdWlyZWpzL2lzc3Vlcy8xODcKICAgICAgICAgICAgICAgICAgICAvL2lmIHdlIGNhbiBOT1QgZmluZCBbbmF0aXZlIGNvZGVdIHRoZW4gaXQgbXVzdCBOT1QgbmF0aXZlbHkgc3VwcG9ydGVkLgogICAgICAgICAgICAgICAgICAgIC8vaW4gSUU4LCBub2RlLmF0dGFjaEV2ZW50IGRvZXMgbm90IGhhdmUgdG9TdHJpbmcoKQogICAgICAgICAgICAgICAgICAgIC8vTm90ZSB0aGUgdGVzdCBmb3IgIltuYXRpdmUgY29kZSIgd2l0aCBubyBjbG9zaW5nIGJyYWNlLCBzZWU6CiAgICAgICAgICAgICAgICAgICAgLy9odHRwczovL2dpdGh1Yi5jb20vanJidXJrZS9yZXF1aXJlanMvaXNzdWVzLzI3MwogICAgICAgICAgICAgICAgICAgICEobm9kZS5hdHRhY2hFdmVudC50b1N0cmluZyAmJiBub2RlLmF0dGFjaEV2ZW50LnRvU3RyaW5nKCkuaW5kZXhPZignW25hdGl2ZSBjb2RlJykgPCAwKSAmJgogICAgICAgICAgICAgICAgICAgICFpc09wZXJhKSB7CiAgICAgICAgICAgICAgICAvL1Byb2JhYmx5IElFLiBJRSAoYXQgbGVhc3QgNi04KSBkbyBub3QgZmlyZQogICAgICAgICAgICAgICAgLy9zY3JpcHQgb25sb2FkIHJpZ2h0IGFmdGVyIGV4ZWN1dGluZyB0aGUgc2NyaXB0LCBzbwogICAgICAgICAgICAgICAgLy93ZSBjYW5ub3QgdGllIHRoZSBhbm9ueW1vdXMgZGVmaW5lIGNhbGwgdG8gYSBuYW1lLgogICAgICAgICAgICAgICAgLy9Ib3dldmVyLCBJRSByZXBvcnRzIHRoZSBzY3JpcHQgYXMgYmVpbmcgaW4gJ2ludGVyYWN0aXZlJwogICAgICAgICAgICAgICAgLy9yZWFkeVN0YXRlIGF0IHRoZSB0aW1lIG9mIHRoZSBkZWZpbmUgY2FsbC4KICAgICAgICAgICAgICAgIHVzZUludGVyYWN0aXZlID0gdHJ1ZTsKCiAgICAgICAgICAgICAgICBub2RlLmF0dGFjaEV2ZW50KCdvbnJlYWR5c3RhdGVjaGFuZ2UnLCBjb250ZXh0Lm9uU2NyaXB0TG9hZCk7CiAgICAgICAgICAgICAgICAvL0l0IHdvdWxkIGJlIGdyZWF0IHRvIGFkZCBhbiBlcnJvciBoYW5kbGVyIGhlcmUgdG8gY2F0Y2gKICAgICAgICAgICAgICAgIC8vNDA0cyBpbiBJRTkrLiBIb3dldmVyLCBvbnJlYWR5c3RhdGVjaGFuZ2Ugd2lsbCBmaXJlIGJlZm9yZQogICAgICAgICAgICAgICAgLy90aGUgZXJyb3IgaGFuZGxlciwgc28gdGhhdCBkb2VzIG5vdCBoZWxwLiBJZiBhZGRFdmVudExpc3RlbmVyCiAgICAgICAgICAgICAgICAvL2lzIHVzZWQsIHRoZW4gSUUgd2lsbCBmaXJlIGVycm9yIGJlZm9yZSBsb2FkLCBidXQgd2UgY2Fubm90CiAgICAgICAgICAgICAgICAvL3VzZSB0aGF0IHBhdGh3YXkgZ2l2ZW4gdGhlIGNvbm5lY3QubWljcm9zb2Z0LmNvbSBpc3N1ZQogICAgICAgICAgICAgICAgLy9tZW50aW9uZWQgYWJvdmUgYWJvdXQgbm90IGRvaW5nIHRoZSAnc2NyaXB0IGV4ZWN1dGUsCiAgICAgICAgICAgICAgICAvL3RoZW4gZmlyZSB0aGUgc2NyaXB0IGxvYWQgZXZlbnQgbGlzdGVuZXIgYmVmb3JlIGV4ZWN1dGUKICAgICAgICAgICAgICAgIC8vbmV4dCBzY3JpcHQnIHRoYXQgb3RoZXIgYnJvd3NlcnMgZG8uCiAgICAgICAgICAgICAgICAvL0Jlc3QgaG9wZTogSUUxMCBmaXhlcyB0aGUgaXNzdWVzLAogICAgICAgICAgICAgICAgLy9hbmQgdGhlbiBkZXN0cm95cyBhbGwgaW5zdGFsbHMgb2YgSUUgNi05LgogICAgICAgICAgICAgICAgLy9ub2RlLmF0dGFjaEV2ZW50KCdvbmVycm9yJywgY29udGV4dC5vblNjcmlwdEVycm9yKTsKICAgICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgICAgIG5vZGUuYWRkRXZlbnRMaXN0ZW5lcignbG9hZCcsIGNvbnRleHQub25TY3JpcHRMb2FkLCBmYWxzZSk7CiAgICAgICAgICAgICAgICBub2RlLmFkZEV2ZW50TGlzdGVuZXIoJ2Vycm9yJywgY29udGV4dC5vblNjcmlwdEVycm9yLCBmYWxzZSk7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgbm9kZS5zcmMgPSB1cmw7CgogICAgICAgICAgICAvL0ZvciBzb21lIGNhY2hlIGNhc2VzIGluIElFIDYtOCwgdGhlIHNjcmlwdCBleGVjdXRlcyBiZWZvcmUgdGhlIGVuZAogICAgICAgICAgICAvL29mIHRoZSBhcHBlbmRDaGlsZCBleGVjdXRpb24sIHNvIHRvIHRpZSBhbiBhbm9ueW1vdXMgZGVmaW5lCiAgICAgICAgICAgIC8vY2FsbCB0byB0aGUgbW9kdWxlIG5hbWUgKHdoaWNoIGlzIHN0b3JlZCBvbiB0aGUgbm9kZSksIGhvbGQgb24KICAgICAgICAgICAgLy90byBhIHJlZmVyZW5jZSB0byB0aGlzIG5vZGUsIGJ1dCBjbGVhciBhZnRlciB0aGUgRE9NIGluc2VydGlvbi4KICAgICAgICAgICAgY3VycmVudGx5QWRkaW5nU2NyaXB0ID0gbm9kZTsKICAgICAgICAgICAgaWYgKGJhc2VFbGVtZW50KSB7CiAgICAgICAgICAgICAgICBoZWFkLmluc2VydEJlZm9yZShub2RlLCBiYXNlRWxlbWVudCk7CiAgICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgICAgICBoZWFkLmFwcGVuZENoaWxkKG5vZGUpOwogICAgICAgICAgICB9CiAgICAgICAgICAgIGN1cnJlbnRseUFkZGluZ1NjcmlwdCA9IG51bGw7CgogICAgICAgICAgICByZXR1cm4gbm9kZTsKICAgICAgICB9IGVsc2UgaWYgKGlzV2ViV29ya2VyKSB7CiAgICAgICAgICAgIHRyeSB7CiAgICAgICAgICAgICAgICAvL0luIGEgd2ViIHdvcmtlciwgdXNlIGltcG9ydFNjcmlwdHMuIFRoaXMgaXMgbm90IGEgdmVyeQogICAgICAgICAgICAgICAgLy9lZmZpY2llbnQgdXNlIG9mIGltcG9ydFNjcmlwdHMsIGltcG9ydFNjcmlwdHMgd2lsbCBibG9jayB1bnRpbAogICAgICAgICAgICAgICAgLy9pdHMgc2NyaXB0IGlzIGRvd25sb2FkZWQgYW5kIGV2YWx1YXRlZC4gSG93ZXZlciwgaWYgd2ViIHdvcmtlcnMKICAgICAgICAgICAgICAgIC8vYXJlIGluIHBsYXksIHRoZSBleHBlY3RhdGlvbiBpcyB0aGF0IGEgYnVpbGQgaGFzIGJlZW4gZG9uZSBzbwogICAgICAgICAgICAgICAgLy90aGF0IG9ubHkgb25lIHNjcmlwdCBuZWVkcyB0byBiZSBsb2FkZWQgYW55d2F5LiBUaGlzIG1heSBuZWVkCiAgICAgICAgICAgICAgICAvL3RvIGJlIHJlZXZhbHVhdGVkIGlmIG90aGVyIHVzZSBjYXNlcyBiZWNvbWUgY29tbW9uLgogICAgICAgICAgICAgICAgaW1wb3J0U2NyaXB0cyh1cmwpOwoKICAgICAgICAgICAgICAgIC8vQWNjb3VudCBmb3IgYW5vbnltb3VzIG1vZHVsZXMKICAgICAgICAgICAgICAgIGNvbnRleHQuY29tcGxldGVMb2FkKG1vZHVsZU5hbWUpOwogICAgICAgICAgICB9IGNhdGNoIChlKSB7CiAgICAgICAgICAgICAgICBjb250ZXh0Lm9uRXJyb3IobWFrZUVycm9yKCdpbXBvcnRzY3JpcHRzJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnaW1wb3J0U2NyaXB0cyBmYWlsZWQgZm9yICcgKwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtb2R1bGVOYW1lICsgJyBhdCAnICsgdXJsLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgW21vZHVsZU5hbWVdKSk7CiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICB9OwoKICAgIGZ1bmN0aW9uIGdldEludGVyYWN0aXZlU2NyaXB0KCkgewogICAgICAgIGlmIChpbnRlcmFjdGl2ZVNjcmlwdCAmJiBpbnRlcmFjdGl2ZVNjcmlwdC5yZWFkeVN0YXRlID09PSAnaW50ZXJhY3RpdmUnKSB7CiAgICAgICAgICAgIHJldHVybiBpbnRlcmFjdGl2ZVNjcmlwdDsKICAgICAgICB9CgogICAgICAgIGVhY2hSZXZlcnNlKHNjcmlwdHMoKSwgZnVuY3Rpb24gKHNjcmlwdCkgewogICAgICAgICAgICBpZiAoc2NyaXB0LnJlYWR5U3RhdGUgPT09ICdpbnRlcmFjdGl2ZScpIHsKICAgICAgICAgICAgICAgIHJldHVybiAoaW50ZXJhY3RpdmVTY3JpcHQgPSBzY3JpcHQpOwogICAgICAgICAgICB9CiAgICAgICAgfSk7CiAgICAgICAgcmV0dXJuIGludGVyYWN0aXZlU2NyaXB0OwogICAgfQoKICAgIC8vTG9vayBmb3IgYSBkYXRhLW1haW4gc2NyaXB0IGF0dHJpYnV0ZSwgd2hpY2ggY291bGQgYWxzbyBhZGp1c3QgdGhlIGJhc2VVcmwuCiAgICBpZiAoaXNCcm93c2VyICYmICFjZmcuc2tpcERhdGFNYWluKSB7CiAgICAgICAgLy9GaWd1cmUgb3V0IGJhc2VVcmwuIEdldCBpdCBmcm9tIHRoZSBzY3JpcHQgdGFnIHdpdGggcmVxdWlyZS5qcyBpbiBpdC4KICAgICAgICBlYWNoUmV2ZXJzZShzY3JpcHRzKCksIGZ1bmN0aW9uIChzY3JpcHQpIHsKICAgICAgICAgICAgLy9TZXQgdGhlICdoZWFkJyB3aGVyZSB3ZSBjYW4gYXBwZW5kIGNoaWxkcmVuIGJ5CiAgICAgICAgICAgIC8vdXNpbmcgdGhlIHNjcmlwdCdzIHBhcmVudC4KICAgICAgICAgICAgaWYgKCFoZWFkKSB7CiAgICAgICAgICAgICAgICBoZWFkID0gc2NyaXB0LnBhcmVudE5vZGU7CiAgICAgICAgICAgIH0KCiAgICAgICAgICAgIC8vTG9vayBmb3IgYSBkYXRhLW1haW4gYXR0cmlidXRlIHRvIHNldCBtYWluIHNjcmlwdCBmb3IgdGhlIHBhZ2UKICAgICAgICAgICAgLy90byBsb2FkLiBJZiBpdCBpcyB0aGVyZSwgdGhlIHBhdGggdG8gZGF0YSBtYWluIGJlY29tZXMgdGhlCiAgICAgICAgICAgIC8vYmFzZVVybCwgaWYgaXQgaXMgbm90IGFscmVhZHkgc2V0LgogICAgICAgICAgICBkYXRhTWFpbiA9IHNjcmlwdC5nZXRBdHRyaWJ1dGUoJ2RhdGEtbWFpbicpOwogICAgICAgICAgICBpZiAoZGF0YU1haW4pIHsKICAgICAgICAgICAgICAgIC8vUHJlc2VydmUgZGF0YU1haW4gaW4gY2FzZSBpdCBpcyBhIHBhdGggKGkuZS4gY29udGFpbnMgJz8nKQogICAgICAgICAgICAgICAgbWFpblNjcmlwdCA9IGRhdGFNYWluOwoKICAgICAgICAgICAgICAgIC8vU2V0IGZpbmFsIGJhc2VVcmwgaWYgdGhlcmUgaXMgbm90IGFscmVhZHkgYW4gZXhwbGljaXQgb25lLgogICAgICAgICAgICAgICAgaWYgKCFjZmcuYmFzZVVybCkgewogICAgICAgICAgICAgICAgICAgIC8vUHVsbCBvZmYgdGhlIGRpcmVjdG9yeSBvZiBkYXRhLW1haW4gZm9yIHVzZSBhcyB0aGUKICAgICAgICAgICAgICAgICAgICAvL2Jhc2VVcmwuCiAgICAgICAgICAgICAgICAgICAgc3JjID0gbWFpblNjcmlwdC5zcGxpdCgnLycpOwogICAgICAgICAgICAgICAgICAgIG1haW5TY3JpcHQgPSBzcmMucG9wKCk7CiAgICAgICAgICAgICAgICAgICAgc3ViUGF0aCA9IHNyYy5sZW5ndGggPyBzcmMuam9pbignLycpICArICcvJyA6ICcuLyc7CgogICAgICAgICAgICAgICAgICAgIGNmZy5iYXNlVXJsID0gc3ViUGF0aDsKICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICAvL1N0cmlwIG9mZiBhbnkgdHJhaWxpbmcgLmpzIHNpbmNlIG1haW5TY3JpcHQgaXMgbm93CiAgICAgICAgICAgICAgICAvL2xpa2UgYSBtb2R1bGUgbmFtZS4KICAgICAgICAgICAgICAgIG1haW5TY3JpcHQgPSBtYWluU2NyaXB0LnJlcGxhY2UoanNTdWZmaXhSZWdFeHAsICcnKTsKCiAgICAgICAgICAgICAgICAvL0lmIG1haW5TY3JpcHQgaXMgc3RpbGwgYSBwYXRoLCBmYWxsIGJhY2sgdG8gZGF0YU1haW4KICAgICAgICAgICAgICAgIGlmIChyZXEuanNFeHRSZWdFeHAudGVzdChtYWluU2NyaXB0KSkgewogICAgICAgICAgICAgICAgICAgIG1haW5TY3JpcHQgPSBkYXRhTWFpbjsKICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICAvL1B1dCB0aGUgZGF0YS1tYWluIHNjcmlwdCBpbiB0aGUgZmlsZXMgdG8gbG9hZC4KICAgICAgICAgICAgICAgIGNmZy5kZXBzID0gY2ZnLmRlcHMgPyBjZmcuZGVwcy5jb25jYXQobWFpblNjcmlwdCkgOiBbbWFpblNjcmlwdF07CgogICAgICAgICAgICAgICAgcmV0dXJuIHRydWU7CiAgICAgICAgICAgIH0KICAgICAgICB9KTsKICAgIH0KCiAgICAvKioKICAgICAqIFRoZSBmdW5jdGlvbiB0aGF0IGhhbmRsZXMgZGVmaW5pdGlvbnMgb2YgbW9kdWxlcy4gRGlmZmVycyBmcm9tCiAgICAgKiByZXF1aXJlKCkgaW4gdGhhdCBhIHN0cmluZyBmb3IgdGhlIG1vZHVsZSBzaG91bGQgYmUgdGhlIGZpcnN0IGFyZ3VtZW50LAogICAgICogYW5kIHRoZSBmdW5jdGlvbiB0byBleGVjdXRlIGFmdGVyIGRlcGVuZGVuY2llcyBhcmUgbG9hZGVkIHNob3VsZAogICAgICogcmV0dXJuIGEgdmFsdWUgdG8gZGVmaW5lIHRoZSBtb2R1bGUgY29ycmVzcG9uZGluZyB0byB0aGUgZmlyc3QgYXJndW1lbnQncwogICAgICogbmFtZS4KICAgICAqLwogICAgZGVmaW5lID0gZnVuY3Rpb24gKG5hbWUsIGRlcHMsIGNhbGxiYWNrKSB7CiAgICAgICAgdmFyIG5vZGUsIGNvbnRleHQ7CgogICAgICAgIC8vQWxsb3cgZm9yIGFub255bW91cyBtb2R1bGVzCiAgICAgICAgaWYgKHR5cGVvZiBuYW1lICE9PSAnc3RyaW5nJykgewogICAgICAgICAgICAvL0FkanVzdCBhcmdzIGFwcHJvcHJpYXRlbHkKICAgICAgICAgICAgY2FsbGJhY2sgPSBkZXBzOwogICAgICAgICAgICBkZXBzID0gbmFtZTsKICAgICAgICAgICAgbmFtZSA9IG51bGw7CiAgICAgICAgfQoKICAgICAgICAvL1RoaXMgbW9kdWxlIG1heSBub3QgaGF2ZSBkZXBlbmRlbmNpZXMKICAgICAgICBpZiAoIWlzQXJyYXkoZGVwcykpIHsKICAgICAgICAgICAgY2FsbGJhY2sgPSBkZXBzOwogICAgICAgICAgICBkZXBzID0gbnVsbDsKICAgICAgICB9CgogICAgICAgIC8vSWYgbm8gbmFtZSwgYW5kIGNhbGxiYWNrIGlzIGEgZnVuY3Rpb24sIHRoZW4gZmlndXJlIG91dCBpZiBpdCBhCiAgICAgICAgLy9Db21tb25KUyB0aGluZyB3aXRoIGRlcGVuZGVuY2llcy4KICAgICAgICBpZiAoIWRlcHMgJiYgaXNGdW5jdGlvbihjYWxsYmFjaykpIHsKICAgICAgICAgICAgZGVwcyA9IFtdOwogICAgICAgICAgICAvL1JlbW92ZSBjb21tZW50cyBmcm9tIHRoZSBjYWxsYmFjayBzdHJpbmcsCiAgICAgICAgICAgIC8vbG9vayBmb3IgcmVxdWlyZSBjYWxscywgYW5kIHB1bGwgdGhlbSBpbnRvIHRoZSBkZXBlbmRlbmNpZXMsCiAgICAgICAgICAgIC8vYnV0IG9ubHkgaWYgdGhlcmUgYXJlIGZ1bmN0aW9uIGFyZ3MuCiAgICAgICAgICAgIGlmIChjYWxsYmFjay5sZW5ndGgpIHsKICAgICAgICAgICAgICAgIGNhbGxiYWNrCiAgICAgICAgICAgICAgICAgICAgLnRvU3RyaW5nKCkKICAgICAgICAgICAgICAgICAgICAucmVwbGFjZShjb21tZW50UmVnRXhwLCAnJykKICAgICAgICAgICAgICAgICAgICAucmVwbGFjZShjanNSZXF1aXJlUmVnRXhwLCBmdW5jdGlvbiAobWF0Y2gsIGRlcCkgewogICAgICAgICAgICAgICAgICAgICAgICBkZXBzLnB1c2goZGVwKTsKICAgICAgICAgICAgICAgICAgICB9KTsKCiAgICAgICAgICAgICAgICAvL01heSBiZSBhIENvbW1vbkpTIHRoaW5nIGV2ZW4gd2l0aG91dCByZXF1aXJlIGNhbGxzLCBidXQgc3RpbGwKICAgICAgICAgICAgICAgIC8vY291bGQgdXNlIGV4cG9ydHMsIGFuZCBtb2R1bGUuIEF2b2lkIGRvaW5nIGV4cG9ydHMgYW5kIG1vZHVsZQogICAgICAgICAgICAgICAgLy93b3JrIHRob3VnaCBpZiBpdCBqdXN0IG5lZWRzIHJlcXVpcmUuCiAgICAgICAgICAgICAgICAvL1JFUVVJUkVTIHRoZSBmdW5jdGlvbiB0byBleHBlY3QgdGhlIENvbW1vbkpTIHZhcmlhYmxlcyBpbiB0aGUKICAgICAgICAgICAgICAgIC8vb3JkZXIgbGlzdGVkIGJlbG93LgogICAgICAgICAgICAgICAgZGVwcyA9IChjYWxsYmFjay5sZW5ndGggPT09IDEgPyBbJ3JlcXVpcmUnXSA6IFsncmVxdWlyZScsICdleHBvcnRzJywgJ21vZHVsZSddKS5jb25jYXQoZGVwcyk7CiAgICAgICAgICAgIH0KICAgICAgICB9CgogICAgICAgIC8vSWYgaW4gSUUgNi04IGFuZCBoaXQgYW4gYW5vbnltb3VzIGRlZmluZSgpIGNhbGwsIGRvIHRoZSBpbnRlcmFjdGl2ZQogICAgICAgIC8vd29yay4KICAgICAgICBpZiAodXNlSW50ZXJhY3RpdmUpIHsKICAgICAgICAgICAgbm9kZSA9IGN1cnJlbnRseUFkZGluZ1NjcmlwdCB8fCBnZXRJbnRlcmFjdGl2ZVNjcmlwdCgpOwogICAgICAgICAgICBpZiAobm9kZSkgewogICAgICAgICAgICAgICAgaWYgKCFuYW1lKSB7CiAgICAgICAgICAgICAgICAgICAgbmFtZSA9IG5vZGUuZ2V0QXR0cmlidXRlKCdkYXRhLXJlcXVpcmVtb2R1bGUnKTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIGNvbnRleHQgPSBjb250ZXh0c1tub2RlLmdldEF0dHJpYnV0ZSgnZGF0YS1yZXF1aXJlY29udGV4dCcpXTsKICAgICAgICAgICAgfQogICAgICAgIH0KCiAgICAgICAgLy9BbHdheXMgc2F2ZSBvZmYgZXZhbHVhdGluZyB0aGUgZGVmIGNhbGwgdW50aWwgdGhlIHNjcmlwdCBvbmxvYWQgaGFuZGxlci4KICAgICAgICAvL1RoaXMgYWxsb3dzIG11bHRpcGxlIG1vZHVsZXMgdG8gYmUgaW4gYSBmaWxlIHdpdGhvdXQgcHJlbWF0dXJlbHkKICAgICAgICAvL3RyYWNpbmcgZGVwZW5kZW5jaWVzLCBhbmQgYWxsb3dzIGZvciBhbm9ueW1vdXMgbW9kdWxlIHN1cHBvcnQsCiAgICAgICAgLy93aGVyZSB0aGUgbW9kdWxlIG5hbWUgaXMgbm90IGtub3duIHVudGlsIHRoZSBzY3JpcHQgb25sb2FkIGV2ZW50CiAgICAgICAgLy9vY2N1cnMuIElmIG5vIGNvbnRleHQsIHVzZSB0aGUgZ2xvYmFsIHF1ZXVlLCBhbmQgZ2V0IGl0IHByb2Nlc3NlZAogICAgICAgIC8vaW4gdGhlIG9uc2NyaXB0IGxvYWQgY2FsbGJhY2suCiAgICAgICAgaWYgKGNvbnRleHQpIHsKICAgICAgICAgICAgY29udGV4dC5kZWZRdWV1ZS5wdXNoKFtuYW1lLCBkZXBzLCBjYWxsYmFja10pOwogICAgICAgICAgICBjb250ZXh0LmRlZlF1ZXVlTWFwW25hbWVdID0gdHJ1ZTsKICAgICAgICB9IGVsc2UgewogICAgICAgICAgICBnbG9iYWxEZWZRdWV1ZS5wdXNoKFtuYW1lLCBkZXBzLCBjYWxsYmFja10pOwogICAgICAgIH0KICAgIH07CgogICAgZGVmaW5lLmFtZCA9IHsKICAgICAgICBqUXVlcnk6IHRydWUKICAgIH07CgogICAgLyoqCiAgICAgKiBFeGVjdXRlcyB0aGUgdGV4dC4gTm9ybWFsbHkganVzdCB1c2VzIGV2YWwsIGJ1dCBjYW4gYmUgbW9kaWZpZWQKICAgICAqIHRvIHVzZSBhIGJldHRlciwgZW52aXJvbm1lbnQtc3BlY2lmaWMgY2FsbC4gT25seSB1c2VkIGZvciB0cmFuc3BpbGluZwogICAgICogbG9hZGVyIHBsdWdpbnMsIG5vdCBmb3IgcGxhaW4gSlMgbW9kdWxlcy4KICAgICAqIEBwYXJhbSB7U3RyaW5nfSB0ZXh0IHRoZSB0ZXh0IHRvIGV4ZWN1dGUvZXZhbHVhdGUuCiAgICAgKi8KICAgIHJlcS5leGVjID0gZnVuY3Rpb24gKHRleHQpIHsKICAgICAgICAvKmpzbGludCBldmlsOiB0cnVlICovCiAgICAgICAgcmV0dXJuIGV2YWwodGV4dCk7CiAgICB9OwoKICAgIC8vU2V0IHVwIHdpdGggY29uZmlnIGluZm8uCiAgICByZXEoY2ZnKTsKfSh0aGlzKSk7Cg==",
"ok": true,
"headers": [
[
"content-type",
"application/javascript"
]
],
"status": 200,
"status_text": ""
}
},
"base_uri": "https://localhost:8080/",
"height": 1000
}
},
"source": [
"# use all predictors at each timestep\n",
"# without selection criteria you would expect performance to be lower, and indeed it is\n",
"configure_plotly_browser_state()\n",
"backtestmodel = BacktestModel(X, Y,\n",
" create_model=LinearRegression,\n",
" coef_dict_param=\"all\",\n",
" startindex=FIRST_TRAIN_MONTHS,\n",
" fit_missing='mean',\n",
" scaler=None)\n",
"backtestmodel.gen_predictions(verbose=False)\n",
"backtestmodel.gen_returns(calc_returns, verbose=False)\n",
"backtestmodel.report_returns(start_date=start_date_str, freq='M')\n",
"backtestmodel.evaluate_predictions()\n",
"backtestmodel.evaluate_quantiles(chart=True, verbose=False)\n",
"perf_all_preds = backtestmodel.cumulative_return\n",
"mychart([perf_all_preds],[\"All preds\"], title=\"OLS all predictors\")\n"
],
"execution_count": null,
"outputs": [
{
"output_type": "display_data",
"data": {
"text/html": [
"\n",
" <script src=\"/static/components/requirejs/require.js\"></script>\n",
" <script>\n",
" requirejs.config({\n",
" paths: {\n",
" base: '/static/base',\n",
" plotly: 'https://cdn.plot.ly/plotly-latest.min.js?noext',\n",
" },\n",
" });\n",
" </script>\n",
" "
],
"text/plain": [
"<IPython.core.display.HTML object>"
]
},
"metadata": {}
},
{
"output_type": "stream",
"name": "stdout",
"text": [
"................................................................................\n",
"15:09:25 Still training step 80 of 563\n",
"................................................................................\n",
"15:09:29 Still training step 160 of 563\n",
"................................................................................\n",
"15:09:34 Still training step 240 of 563\n",
"................................................................................\n",
"15:09:39 Still training step 320 of 563\n",
"................................................................................\n",
"15:09:45 Still training step 400 of 563\n",
"................................................................................\n",
"15:09:50 Still training step 480 of 563\n",
"................................................................................\n",
"15:09:56 Still training step 560 of 563\n",
"...\n",
"Mean return: 2.781%\n",
"Monthly Sharpe ratio: 0.506\n",
"OOS MSE across all predictions: 43.8692\n",
"In-sample MSE: 34.7467\n",
"Variance: 39.4097\n",
"R-squared: -0.1132\n",
"Avg rank correlation (Kendall's tau): 0.0246 (Expected: 0)\n",
"5-quintile accuracy: 0.2161 (Expected: 0.2)\n",
"Long/short/flat accuracy: 0.4657 (Expected: 0.44)\n",
"Excess true positive in quintiles 1 + 5: 225.800000\n"
]
},
{
"output_type": "display_data",
"data": {
"image/png": "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iIiIRCrN4REREREJH0p4REREJOxpSEtERCRSadKyiIiISPhQhUdERCRSadKyiIiISPhQwiMiIhKpnDdwWxnMrK2ZLSuxZZrZcN9rw8xsjZn9bGZPlNhnlJmtN7O1Zta3vFPVkJaIiIgElXNuLdAZwMw8QDIww8xOBy4AjnXO5ZpZA1+fY4DLgfZAY2C2mR3lnCss7T2U8IiIiESq0JzD0xvY4JzbbGZPAmOdc7kAzrkdvj4XAO/42jea2XqgOzCvtINqSEtERERCyeXANN/jo4BeZrbAzL41s+N97U2ArSX2SfK1lUoVHhERkUgVwAqPmQ0EBpZomuycm3xQn1igPzDK1xQN1AF6AscD082s5V95fyU8IiIi4ne+5GZyOd3OBpY451J9z5OAD5xzDvjJzLxAPYrm+DQrsV9TX1upNKQlIiISqZwL3FYxV/D7cBbAh8DpAGZ2FBAL7AQ+Bi43sypm1gJoA/xU1oFV4REREZGgM7NqQB9gUInmV4BXzOy/QB5wja/a87OZTQdWAQXAkLJWaIESHhERkcgVQqu0nHP7gLoHteUB/1dK/0eBRyt6fA1piYiISNhThUdERCRShVCFx99U4REREZGwpwqPiIhIpCrnHlfhRBUeERERCXtKeERERCTsaUhLREQkUmnSsoiIiEj4UIVHREQkUlX8lg+Vnio8IiIiEvZU4REREYlUETSHJ2QTnuOPGxDsEMLefVEtgh1C2FvWtEuwQ4gIy/fXCnYIYW8RBcEOQeR/ErIJj4iIiPhZBFV4NIdHREREwp4qPCIiIpFKt5YQERERCR+q8IiIiEQo59V1eERERETChio8IiIikUqrtERERETChyo8IiIikUqrtERERETChxIeERERCXsa0hIREYlUWpYuIiIiEj5U4REREYlUWpYuIiIiEj5U4REREYlUqvCIiIiIhA9VeERERCKV0yotERERkbChCo+IiEik0hweERERkfChCo+IiEik0pWWRURERMKHKjwiIiKRymkOj4iIiEjYUIVHREQkUmkOj4iIiEj4UMIjIiIiYU9DWiIiIhHK6cKDIiIiIuFDFR4REZFIpUnLIiIiIuFDFR4REZFIpQsPioiIiIQPVXhEREQilebwiIiIiIQPVXhEREQila7DIyIiIhI+VOERERGJVJrDIyIiIhI+VOERERGJVLoOj4iIiEhgmFlb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UYmIiIgcRuUmPM65T30PM4DT/RuOiIiIBIo3gu5aWZFVWlM5xAUIfXN5REREREJeRYa0Pi3xuCpwEUXzeERERKQS82oOz++cc++XfG5m04Dv/RaRiIiIyGH2V+6W3gZocLgDEREREfGXiszh2cuBc3i2U3TlZREREanEtCy9BOdcQiACEREREfGXcoe0zOzrirSJiIhI5RJJt5YotcJjZlWBeKCemdWG4rpXDaBJAGITEREROSzKGtIaBAwHGgOL+T3hyQQm+DkuERER8TPN4QGcc88Cz5rZMOfc8wGMSUREROSwqsiydK+Z1frtiZnVNrPBfoxJREREAiCS5vBUJOEZ4Jzb89sT59xuYID/QhIRERE5vCpyawmPmZlzzgGYmQeI9W9YIiIi4m+hUHkJlIokPF8A/zazl3zPB/naRERERCqFiiQ8dwEDgZt9z2cBU/wWkYiIiAREJK3SKncOj3PO65x70Tl3iXPuEmAVoFVbIiIiUmlUpMKDmXUBrgAuAzYCH/gzKBEREfE/b+QUeMq80vJRFCU5VwA7gX8D5pw7PUCxiYiIiBwWZVV41gDfAec559YDmNmIgEQlIiIifufVHB4A/gZsA74xsylm1hsi6DcjIiIiYaPUhMc596Fz7nKgHfANRffVamBmk8zsrEAFKCIiIvK/qsgqrX3Oubedc+cDTYGlFC1VFxERkUrMBXALtorcWqKYc263c26yc663vwISEREROdwqtCxdREREwk8k3VriT1V4RERERCojVXhEREQilNciZ/G1KjwiIiIS9lThERERiVChsHoqUFThERERkbCnCo+IiEiE0iotERERkTCiCo+IiEiE8kbOIi1VeERERCT8qcIjIiISobxETolHFR4REREJe6rwiIiIRChdh0dEREQkjCjhERERkbAXskNaa3J3BDuEsDc7tmWwQwh7N/0jM9ghRISv364d7BDCXrc6rYMdgviBlqWLiIiIhJGQrfCIiIiIf+nWEiIiIiJhRBUeERGRCKVl6SIiIiJhRBUeERGRCKVVWiIiIiJhRAmPiIhIhPIGcCuPmdUys/fMbI2ZrTazE8yss5nNN7NlZrbIzLr7+pqZPWdm681shZkdV97xNaQlIiIioeBZ4Avn3CVmFgvEA9OBB51zn5vZOcATwGnA2UAb39YDmOT7WSolPCIiIhEqVK7DY2Y1gVOA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\n",
"text/plain": [
"<Figure size 720x720 with 2 Axes>"
]
},
"metadata": {
"needs_background": "light"
}
},
{
"output_type": "display_data",
"data": {
"text/html": [
"<div id=\"07db0d9a-3174-4150-afd3-786a2b4e917f\" style=\"height: 600px; width: 900px;\" class=\"plotly-graph-div\"></div><script type=\"text/javascript\">require([\"plotly\"], function(Plotly) { window.PLOTLYENV=window.PLOTLYENV || {};window.PLOTLYENV.BASE_URL=\"https://plot.ly\";Plotly.newPlot(\"07db0d9a-3174-4150-afd3-786a2b4e917f\", [{\"type\": \"scatter\", \"x\": [1970.0, 1970.081850533808, 1970.1637010676156, 1970.2455516014236, 1970.3274021352313, 1970.4092526690392, 1970.491103202847, 1970.5729537366549, 1970.6548042704626, 1970.7366548042705, 1970.8185053380782, 1970.9003558718862, 1970.982206405694, 1971.0640569395018, 1971.1459074733095, 1971.2277580071175, 1971.3096085409252, 1971.3914590747331, 1971.4733096085408, 1971.5551601423488, 1971.6370106761565, 1971.7188612099644, 1971.8007117437724, 1971.88256227758, 1971.964412811388, 1972.0462633451957, 1972.1281138790036, 1972.2099644128114, 1972.2918149466193, 1972.373665480427, 1972.455516014235, 1972.5373665480427, 1972.6192170818506, 1972.7010676156583, 1972.7829181494662, 1972.864768683274, 1972.946619217082, 1973.0284697508896, 1973.1103202846975, 1973.1921708185052, 1973.2740213523132, 1973.355871886121, 1973.4377224199288, 1973.5195729537365, 1973.6014234875445, 1973.6832740213524, 1973.7651245551601, 1973.846975088968, 1973.9288256227758, 1974.0106761565837, 1974.0925266903914, 1974.1743772241994, 1974.256227758007, 1974.338078291815, 1974.4199288256227, 1974.5017793594307, 1974.5836298932384, 1974.6654804270463, 1974.747330960854, 1974.829181494662, 1974.9110320284697, 1974.9928825622776, 1975.0747330960853, 1975.1565836298932, 1975.238434163701, 1975.320284697509, 1975.4021352313168, 1975.4839857651245, 1975.5658362989325, 1975.6476868327402, 1975.7295373665481, 1975.8113879003558, 1975.8932384341638, 1975.9750889679715, 1976.0569395017794, 1976.1387900355871, 1976.220640569395, 1976.3024911032028, 1976.3843416370107, 1976.4661921708184, 1976.5480427046264, 1976.629893238434, 1976.711743772242, 1976.7935943060497, 1976.8754448398577, 1976.9572953736654, 1977.0391459074733, 1977.1209964412812, 1977.202846975089, 1977.284697508897, 1977.3665480427046, 1977.4483985765125, 1977.5302491103203, 1977.6120996441282, 1977.693950177936, 1977.7758007117438, 1977.8576512455516, 1977.9395017793595, 1978.0213523131672, 1978.1032028469751, 1978.1850533807828, 1978.2669039145908, 1978.3487544483985, 1978.4306049822064, 1978.5124555160141, 1978.594306049822, 1978.6761565836298, 1978.7580071174377, 1978.8398576512454, 1978.9217081850534, 1979.0035587188613, 1979.085409252669, 1979.167259786477, 1979.2491103202847, 1979.3309608540926, 1979.4128113879003, 1979.4946619217083, 1979.576512455516, 1979.658362989324, 1979.7402135231316, 1979.8220640569396, 1979.9039145907473, 1979.9857651245552, 1980.067615658363, 1980.1494661921708, 1980.2313167259786, 1980.3131672597865, 1980.3950177935942, 1980.4768683274021, 1980.5587188612099, 1980.6405693950178, 1980.7224199288257, 1980.8042704626334, 1980.8861209964414, 1980.967971530249, 1981.049822064057, 1981.1316725978647, 1981.2135231316727, 1981.2953736654804, 1981.3772241992883, 1981.459074733096, 1981.540925266904, 1981.6227758007117, 1981.7046263345196, 1981.7864768683273, 1981.8683274021353, 1981.950177935943, 1982.032028469751, 1982.1138790035586, 1982.1957295373666, 1982.2775800711743, 1982.3594306049822, 1982.4412811387901, 1982.5231316725979, 1982.6049822064058, 1982.6868327402135, 1982.7686832740214, 1982.8505338078292, 1982.932384341637, 1983.0142348754448, 1983.0960854092527, 1983.1779359430604, 1983.2597864768684, 1983.341637010676, 1983.423487544484, 1983.5053380782917, 1983.5871886120997, 1983.6690391459074, 1983.7508896797153, 1983.832740213523, 1983.914590747331, 1983.9964412811387, 1984.0782918149466, 1984.1601423487546, 1984.2419928825623, 1984.3238434163702, 1984.405693950178, 1984.4875444839859, 1984.5693950177936, 1984.6512455516015, 1984.7330960854092, 1984.8149466192172, 1984.8967971530249, 1984.9786476868328, 1985.0604982206405, 1985.1423487544484, 1985.2241992882562, 1985.306049822064, 1985.3879003558718, 1985.4697508896797, 1985.5516014234875, 1985.6334519572954, 1985.715302491103, 1985.797153024911, 1985.8790035587188, 1985.9608540925267, 1986.0427046263346, 1986.1245551601423, 1986.2064056939503, 1986.288256227758, 1986.370106761566, 1986.4519572953736, 1986.5338078291816, 1986.6156583629893, 1986.6975088967972, 1986.779359430605, 1986.8612099644129, 1986.9430604982206, 1987.0249110320285, 1987.1067615658362, 1987.1886120996442, 1987.2704626334519, 1987.3523131672598, 1987.4341637010675, 1987.5160142348755, 1987.5978647686832, 1987.679715302491, 1987.761565836299, 1987.8434163701068, 1987.9252669039147, 1988.0071174377224, 1988.0889679715303, 1988.170818505338, 1988.252669039146, 1988.3345195729537, 1988.4163701067616, 1988.4982206405693, 1988.5800711743773, 1988.661921708185, 1988.743772241993, 1988.8256227758006, 1988.9074733096086, 1988.9893238434163, 1989.0711743772242, 1989.153024911032, 1989.2348754448399, 1989.3167259786476, 1989.3985765124555, 1989.4804270462632, 1989.5622775800712, 1989.644128113879, 1989.7259786476868, 1989.8078291814948, 1989.8896797153025, 1989.9715302491104, 1990.053380782918, 1990.135231316726, 1990.2170818505338, 1990.2989323843417, 1990.3807829181494, 1990.4626334519573, 1990.544483985765, 1990.626334519573, 1990.7081850533807, 1990.7900355871886, 1990.8718861209964, 1990.9537366548043, 1991.035587188612, 1991.11743772242, 1991.1992882562276, 1991.2811387900356, 1991.3629893238435, 1991.4448398576512, 1991.5266903914592, 1991.6085409252669, 1991.6903914590748, 1991.7722419928825, 1991.8540925266905, 1991.9359430604982, 1992.017793594306, 1992.0996441281138, 1992.1814946619218, 1992.2633451957295, 1992.3451957295374, 1992.4270462633451, 1992.508896797153, 1992.5907473309608, 1992.6725978647687, 1992.7544483985764, 1992.8362989323844, 1992.918149466192, 1993.0, 1993.081850533808, 1993.1637010676156, 1993.2455516014236, 1993.3274021352313, 1993.4092526690392, 1993.491103202847, 1993.5729537366549, 1993.6548042704626, 1993.7366548042705, 1993.8185053380782, 1993.9003558718862, 1993.982206405694, 1994.0640569395018, 1994.1459074733095, 1994.2277580071175, 1994.3096085409252, 1994.3914590747331, 1994.4733096085408, 1994.5551601423488, 1994.6370106761565, 1994.7188612099644, 1994.8007117437724, 1994.88256227758, 1994.964412811388, 1995.0462633451957, 1995.1281138790036, 1995.2099644128114, 1995.2918149466193, 1995.373665480427, 1995.455516014235, 1995.5373665480427, 1995.6192170818506, 1995.7010676156583, 1995.7829181494662, 1995.864768683274, 1995.946619217082, 1996.0284697508896, 1996.1103202846975, 1996.1921708185052, 1996.2740213523132, 1996.355871886121, 1996.4377224199288, 1996.5195729537368, 1996.6014234875445, 1996.6832740213524, 1996.7651245551601, 1996.846975088968, 1996.9288256227758, 1997.0106761565837, 1997.0925266903914, 1997.1743772241994, 1997.256227758007, 1997.338078291815, 1997.4199288256227, 1997.5017793594307, 1997.5836298932384, 1997.6654804270463, 1997.747330960854, 1997.829181494662, 1997.9110320284697, 1997.9928825622776, 1998.0747330960853, 1998.1565836298932, 1998.238434163701, 1998.320284697509, 1998.4021352313168, 1998.4839857651245, 1998.5658362989325, 1998.6476868327402, 1998.7295373665481, 1998.8113879003558, 1998.8932384341638, 1998.9750889679715, 1999.0569395017794, 1999.1387900355871, 1999.220640569395, 1999.3024911032028, 1999.3843416370107, 1999.4661921708184, 1999.5480427046264, 1999.629893238434, 1999.711743772242, 1999.7935943060497, 1999.8754448398577, 1999.9572953736654, 2000.0391459074733, 2000.1209964412812, 2000.202846975089, 2000.284697508897, 2000.3665480427046, 2000.4483985765125, 2000.5302491103203, 2000.6120996441282, 2000.693950177936, 2000.7758007117438, 2000.8576512455516, 2000.9395017793595, 2001.0213523131672, 2001.1032028469751, 2001.1850533807828, 2001.2669039145908, 2001.3487544483985, 2001.4306049822064, 2001.5124555160141, 2001.594306049822, 2001.6761565836298, 2001.7580071174377, 2001.8398576512454, 2001.9217081850534, 2002.0035587188613, 2002.085409252669, 2002.167259786477, 2002.2491103202847, 2002.3309608540926, 2002.4128113879003, 2002.4946619217083, 2002.576512455516, 2002.658362989324, 2002.7402135231316, 2002.8220640569396, 2002.9039145907473, 2002.9857651245552, 2003.067615658363, 2003.1494661921708, 2003.2313167259786, 2003.3131672597865, 2003.3950177935942, 2003.4768683274021, 2003.5587188612099, 2003.6405693950178, 2003.7224199288257, 2003.8042704626334, 2003.8861209964414, 2003.967971530249, 2004.049822064057, 2004.1316725978647, 2004.2135231316727, 2004.2953736654804, 2004.3772241992883, 2004.459074733096, 2004.540925266904, 2004.6227758007117, 2004.7046263345196, 2004.7864768683273, 2004.8683274021353, 2004.950177935943, 2005.032028469751, 2005.1138790035586, 2005.1957295373666, 2005.2775800711743, 2005.3594306049822, 2005.4412811387901, 2005.5231316725979, 2005.6049822064058, 2005.6868327402135, 2005.7686832740214, 2005.8505338078292, 2005.932384341637, 2006.0142348754448, 2006.0960854092527, 2006.1779359430604, 2006.2597864768684, 2006.341637010676, 2006.423487544484, 2006.5053380782917, 2006.5871886120997, 2006.6690391459074, 2006.7508896797153, 2006.832740213523, 2006.914590747331, 2006.9964412811387, 2007.0782918149466, 2007.1601423487546, 2007.2419928825623, 2007.3238434163702, 2007.405693950178, 2007.4875444839859, 2007.5693950177936, 2007.6512455516015, 2007.7330960854092, 2007.8149466192172, 2007.8967971530249, 2007.9786476868328, 2008.0604982206405, 2008.1423487544484, 2008.2241992882562, 2008.306049822064, 2008.3879003558718, 2008.4697508896797, 2008.5516014234875, 2008.6334519572954, 2008.715302491103, 2008.797153024911, 2008.879003558719, 2008.9608540925267, 2009.0427046263346, 2009.1245551601423, 2009.2064056939503, 2009.288256227758, 2009.370106761566, 2009.4519572953736, 2009.5338078291816, 2009.6156583629893, 2009.6975088967972, 2009.779359430605, 2009.8612099644129, 2009.9430604982206, 2010.0249110320285, 2010.1067615658362, 2010.1886120996442, 2010.2704626334519, 2010.3523131672598, 2010.4341637010675, 2010.5160142348755, 2010.5978647686832, 2010.679715302491, 2010.761565836299, 2010.8434163701068, 2010.9252669039147, 2011.0071174377224, 2011.0889679715303, 2011.170818505338, 2011.252669039146, 2011.3345195729537, 2011.4163701067616, 2011.4982206405693, 2011.5800711743773, 2011.661921708185, 2011.743772241993, 2011.8256227758006, 2011.9074733096086, 2011.9893238434163, 2012.0711743772242, 2012.153024911032, 2012.2348754448399, 2012.3167259786476, 2012.3985765124555, 2012.4804270462632, 2012.5622775800712, 2012.644128113879, 2012.7259786476868, 2012.8078291814948, 2012.8896797153025, 2012.9715302491104, 2013.053380782918, 2013.135231316726, 2013.2170818505338, 2013.2989323843417, 2013.3807829181494, 2013.4626334519573, 2013.544483985765, 2013.626334519573, 2013.7081850533807, 2013.7900355871886, 2013.8718861209964, 2013.9537366548043, 2014.035587188612, 2014.11743772242, 2014.1992882562276, 2014.2811387900356, 2014.3629893238435, 2014.4448398576512, 2014.5266903914592, 2014.6085409252669, 2014.6903914590748, 2014.7722419928825, 2014.8540925266905, 2014.9359430604982, 2015.017793594306, 2015.0996441281138, 2015.1814946619218, 2015.2633451957295, 2015.3451957295374, 2015.4270462633451, 2015.508896797153, 2015.5907473309608, 2015.6725978647687, 2015.7544483985764, 2015.8362989323844, 2015.918149466192, 2016.0], \"y\": [100.0, 99.9175, 100.68853004166667, 100.11376634934548, 102.03344781909418, 103.47381999080704, 101.23878547900563, 103.05095973907983, 103.62461008162738, 101.40358927221116, 102.70831545418027, 102.87521646679332, 104.78440902572288, 105.2332355777164, 107.0984946783314, 105.29388504300152, 105.71155078700542, 106.05158960870362, 107.2234596738798, 105.07452283624914, 105.20586598979445, 105.8590190744811, 106.67677999683147, 106.47676103433741, 107.10852314980781, 108.01269759939744, 107.71476257518577, 108.42747525422492, 110.54723239544502, 111.259340817459, 111.92133389532289, 112.13678246307137, 111.47984781247521, 111.8570212975741, 111.94464263092385, 110.17218578926756, 108.82808512263848, 111.15428544213488, 113.01797229471467, 112.86351439924522, 113.98556583823105, 118.16028718705624, 114.05421720730604, 113.69969868215333, 115.794615630372, 119.41223241369086, 121.84127624137268, 123.29931018039444, 120.8518188733136, 121.67663253712398, 119.98735528873357, 120.86826245547834, 121.78988295670136, 123.07882588465979, 123.51678137343272, 126.21768165946511, 122.42063306954287, 127.27563134269249, 129.35340602436193, 129.03972401475286, 119.36712136881367, 121.24715353037247, 119.59111949173679, 117.14747428345564, 118.51614727466735, 116.6880357029556, 117.81893724897674, 118.84199835408869, 120.27899618418687, 118.77250175697993, 119.34557907795738, 116.84628374276649, 117.03908011094204, 118.57619336306573, 117.43687377183561, 116.055033223787, 119.52024309079393, 121.97738008833548, 121.01680822013982, 121.6329854686607, 122.88985965183686, 122.65944116498966, 122.17084772434912, 121.2891814399384, 121.60251182532494, 121.00564616311564, 121.65000122893423, 122.03421248281563, 124.46371026299437, 126.76940049561631, 128.4966335773691, 131.32677193191066, 131.7426400430284, 131.9830703611069, 129.72286028117296, 130.4168775836772, 129.694150720401, 128.52906493309607, 131.3834812501519, 133.1900041173415, 135.0435650079745, 135.5015877659599, 134.09462961299002, 134.28347954969496, 132.4874380107178, 135.45626068414128, 135.5149583971044, 136.86897868975547, 138.33575791138068, 140.26784733020963, 140.9539908833999, 140.73551219753062, 139.51228603734708, 142.865231311778, 144.53556397453153, 144.4693185077099, 146.6435817512509, 145.51198211207043, 147.40727567908013, 151.17475996664461, 154.27258275629444, 155.37177490843303, 154.08089441190214, 155.53053882682744, 154.71270741016303, 153.98168986765, 156.8034043344747, 158.5204016119372, 161.9378372700212, 162.4735816149895, 159.88754377428424, 157.81300289381292, 158.69938592673316, 161.54274992458713, 160.58157056253583, 157.88246199733055, 157.7719442739324, 156.24155641447527, 156.0280262873755, 156.59882881687682, 154.62698856402432, 155.68231776097377, 154.1527389889722, 154.1296160781239, 153.74172321099394, 157.48149062810134, 160.57600191894352, 163.25628301764056, 166.16496579340486, 167.40012537246918, 172.26588901662893, 172.5443855372058, 170.3286280529322, 174.63368412697008, 179.3604358440067, 177.87772290769627, 181.79251512602315, 174.79653316892336, 175.6588627325567, 178.7855904891962, 179.0150319969907, 181.03939365048998, 180.05122029348104, 180.16075145249292, 181.57801603058584, 179.35671163447836, 173.6950181038833, 174.40572021962504, 172.4451092481561, 174.47421336697607, 172.80798462932145, 173.26880592166629, 175.13866511890427, 173.96085759597966, 175.04231426070135, 171.9032220916261, 169.54958047582159, 170.0031256035944, 168.5609324213906, 167.7139137359731, 169.30160545267367, 169.27903190528, 170.93091312495568, 169.90247879765386, 168.56166506914238, 168.30741789099645, 164.1810810290355, 164.2782215019777, 166.0976028051121, 166.93501155258787, 168.34561240020722, 170.80906986166357, 173.03243458769623, 176.0244537691085, 175.07685545965145, 173.7010431704977, 178.04791177583942, 180.3551159659347, 179.57958896728118, 181.64625073697965, 185.52742562772647, 182.1771095332658, 183.00297909648324, 182.83980144012222, 182.6569616386821, 186.44709359268475, 186.93651721336556, 191.17374493686853, 191.7647904316317, 193.47309510639346, 190.2114611780582, 187.569106963193, 183.8739955560181, 178.71020084748656, 177.41008413632107, 175.27081420511064, 181.7237013480955, 182.20526915666795, 181.37927193649105, 184.4219092232257, 186.20004379798632, 188.7571910661453, 187.02377086152123, 188.7677675248049, 190.3172362832377, 190.74386408790596, 190.99501017562167, 189.44317571794477, 189.9799313824789, 190.90608354796848, 188.43066799796316, 187.1933066114432, 187.9202406187843, 188.34306116017657, 188.23005532348049, 186.80264407061074, 187.15134233954254, 188.589288486518, 190.06971440113716, 192.31570485964392, 195.2709561909871, 197.38964606565932, 197.4011604616798, 196.4602149301458, 193.51494887465137, 194.9211575031405, 195.26064518579184, 199.97944411111516, 208.9168587675144, 213.07604523081102, 208.9832095286692, 212.18065263445786, 210.0853686896926, 212.2667551012539, 214.63529831025872, 212.81626415707927, 207.86473907769124, 206.6158184370661, 206.76905850240695, 206.79662771020725, 205.23875978145702, 205.7296224819343, 206.58854365579643, 208.2739618577883, 213.82619855764716, 218.65867064504997, 219.92871309037997, 218.00433685083914, 218.06247134066604, 219.41991022476165, 219.71064160580943, 218.94165436018912, 214.8748131304486, 216.36461183481964, 219.4856713605369, 219.42348375365142, 223.06774211299333, 225.07535179201022, 227.5680613131067, 226.7488162923795, 227.8787812269032, 225.81647825679974, 228.94968189261283, 227.8564471615756, 227.6380847330458, 228.28685327453496, 230.29577758335085, 232.285916927967, 235.92699867581294, 237.6748245243362, 239.15831155407562, 237.77916529078044, 234.98327860556972, 232.90955117187556, 234.07021710188206, 229.10792849932218, 232.57509515061196, 229.5981339326841, 232.60012953385396, 231.18126874369747, 235.09208520661167, 234.3672179438913, 236.50386574747975, 232.70797870223268, 233.0201952403248, 232.73474550115537, 230.96402197913412, 228.89304458205456, 229.41759114255507, 233.32724925827608, 234.4141653610709, 232.07783751297222, 230.83041913634, 233.6003841659761, 238.86028614944667, 237.92475002869463, 238.2380176162324, 240.70775173218735, 242.6775435005291, 241.1163179706757, 242.9688950137504, 244.1553931177342, 246.09642849302017, 245.58167679675563, 243.887163226858, 248.80148956587917, 248.12557885255853, 245.46649973252195, 248.69642975816907, 246.69235102836785, 246.81775297347394, 251.13706365050973, 250.90476186663304, 259.0173491669875, 270.86307593555773, 275.0117953819707, 281.78166907829024, 277.4422313744846, 279.15081311603245, 279.1391818321526, 281.39323072544727, 279.18663880784186, 281.42478502895136, 291.2793429180485, 290.58755447861813, 279.0633363805869, 275.6936465937913, 274.07394642005283, 274.4119709539709, 270.09912947714434, 271.6116846022163, 271.5822600030511, 271.30615137204796, 271.26997721853166, 270.3973921251454, 271.17703793910624, 273.0278212230407, 285.6030276222052, 281.0262391045594, 274.90923463338345, 294.3911357277359, 298.0342260323666, 295.42891017313366, 300.11145839937785, 301.491971108015, 291.39450250898904, 287.2348459856732, 289.48006503179454, 284.16328117071055, 279.6735013282133, 281.6125709374222, 292.6189289182265, 274.710650468431, 261.0643989064117, 265.7243984268911, 265.1907352600505, 263.24158335588913, 264.60824590947846, 270.0746112562252, 268.9222929148653, 278.02083049181823, 283.21286950125295, 276.55972717521934, 277.37096904159995, 276.6590502210598, 286.57958266356997, 289.04655523766553, 277.47987558557156, 280.8235080863777, 282.68162363154926, 275.7488568119855, 278.32710862317754, 278.90463737357067, 284.59196777068, 285.035456920456, 278.5033943660289, 275.70675611427004, 280.97964782495546, 277.2941314443181, 278.62976484410825, 269.9434819250932, 271.8083414793923, 265.9078353997772, 270.4814501686534, 265.82466120158307, 264.130028986423, 265.7698362497137, 258.062510998472, 260.08830170980997, 255.64946136062923, 256.8637963020922, 253.19706560987987, 254.40608159816702, 256.5473327849516, 259.21756294035504, 263.5356955096698, 262.83732591656917, 262.47811490448316, 258.5693783100306, 255.81561443102876, 252.91850259759735, 257.6291097084776, 256.1284201444257, 263.3853920485178, 262.45915341981384, 263.73864179273545, 267.7035127076862, 267.6700497685977, 275.75145468786127, 272.2631987860598, 272.9120927431666, 272.41175390647084, 277.8032365358697, 277.4305171935174, 274.138341722821, 274.86023935602446, 274.53956907677576, 275.09322387441387, 286.92452494454596, 293.05992770294347, 301.52203311536596, 298.35856445126393, 296.8394220939329, 302.5263706888825, 301.82047582394176, 298.38726791144444, 299.22772538272835, 299.58181152443126, 302.52270630756277, 304.29750618456717, 304.49529956358714, 305.9670268448111, 302.2240302164096, 297.89215244997445, 288.61529433576146, 294.79406676199955, 286.76092844273506, 286.53151969998083, 286.13037557240085, 271.7260955821269, 274.63356480485567, 287.6946794243666, 282.7079716476776, 282.6302269554745, 279.37055833792135, 277.78513041935366, 284.82235372331064, 281.46382346899, 277.80713929575535, 284.8101942655025, 288.4438976606732, 321.86973800125094, 319.1338452282403, 325.22132332596897, 316.9119185149905, 320.01765531643736, 324.9405935807219, 324.9324700658824, 328.1493015195347, 335.13888164190075, 335.38744297911853, 328.7719256663554, 326.1773672196384, 331.63811997584054, 330.97208008488906, 342.07067717040235, 337.99718552309815, 334.57496401967677, 327.6659910126704, 331.6798994025757, 333.2111549381509, 338.12601947348867, 344.2714598774193, 344.03620771316974, 342.9037551961139, 340.68059584992574, 352.2864481485466, 356.6518643851873, 355.3738618711404, 363.3756966609389, 383.7489607203956, 362.2558210120474, 360.41435392190283, 367.42140965273444, 372.58674230343576, 373.5616776124631, 378.84757535067945, 375.7915382428506, 374.47000466669664, 368.75309592878506, 364.3680070296986, 360.569470556414, 362.38133214596, 373.6664907978721, 368.26701000584285, 360.15592911046417, 360.35101357206565, 365.28782245800295, 368.6636907505523, 375.9232932609153, 370.73241911980415, 357.868004176347, 351.0267608298425, 355.8036500001353, 355.65243344888523, 355.2226867584678, 352.7953317322849, 350.32576441015897, 353.50789010355123, 353.74945382845533, 346.9309331059119, 347.14198275688466, 346.70805527843856, 344.89650568960866, 345.70126420288443, 347.1359244493264, 348.8195336829056, 357.2580595685846, 359.44031088244935, 358.8711970568855, 358.6379307787985, 358.4197593709081, 354.6503782348573, 352.94805641933, 350.46859632298424, 360.03054785932966, 377.06599328220693, 372.83656972422483, 362.50899674286387, 368.3604961319549, 362.9855025592295, 357.12631157208585, 370.11380510292406, 365.4935511025559, 367.4489416009546, 382.4592308653536, 385.6017708789639, 378.8762333252167, 371.6081242492613, 376.86947594175706, 375.6352284080478, 372.52058630583105, 362.1924530505019], \"mode\": \"line\", \"name\": \"All preds\"}], {\"title\": \"OLS all predictors\", \"autosize\": false, \"width\": 900, \"height\": 600, \"yaxis\": {\"type\": \"log\", \"autorange\": true}}, {\"showLink\": true, \"linkText\": \"Export to plot.ly\"})});</script>"
],
"text/plain": [
"<IPython.core.display.HTML object>"
]
},
"metadata": {}
}
]
},
{
"cell_type": "code",
"metadata": {
"id": "Ki8T0jBZfkZl",
"outputId": "0fa085f8-b724-4c6e-b2d3-815d1a8715a3",
"colab": {
"resources": {
"http://localhost:8080/static/components/requirejs/require.js": {
"data": "LyoqIHZpbTogZXQ6dHM9NDpzdz00OnN0cz00CiAqIEBsaWNlbnNlIFJlcXVpcmVKUyAyLjEuMjIgQ29weXJpZ2h0IChjKSAyMDEwLTIwMTUsIFRoZSBEb2pvIEZvdW5kYXRpb24gQWxsIFJpZ2h0cyBSZXNlcnZlZC4KICogQXZhaWxhYmxlIHZpYSB0aGUgTUlUIG9yIG5ldyBCU0QgbGljZW5zZS4KICogc2VlOiBodHRwOi8vZ2l0aHViLmNvbS9qcmJ1cmtlL3JlcXVpcmVqcyBmb3IgZGV0YWlscwogKi8KLy9Ob3QgdXNpbmcgc3RyaWN0OiB1bmV2ZW4gc3RyaWN0IHN1cHBvcnQgaW4gYnJvd3NlcnMsICMzOTIsIGFuZCBjYXVzZXMKLy9wcm9ibGVtcyB3aXRoIHJlcXVpcmVqcy5leGVjKCkvdHJhbnNwaWxlciBwbHVnaW5zIHRoYXQgbWF5IG5vdCBiZSBzdHJpY3QuCi8qanNsaW50IHJlZ2V4cDogdHJ1ZSwgbm9tZW46IHRydWUsIHNsb3BweTogdHJ1ZSAqLwovKmdsb2JhbCB3aW5kb3csIG5hdmlnYXRvciwgZG9jdW1lbnQsIGltcG9ydFNjcmlwdHMsIHNldFRpbWVvdXQsIG9wZXJhICovCgp2YXIgcmVxdWlyZWpzLCByZXF1aXJlLCBkZWZpbmU7CihmdW5jdGlvbiAoZ2xvYmFsKSB7CiAgICB2YXIgcmVxLCBzLCBoZWFkLCBiYXNlRWxlbWVudCwgZGF0YU1haW4sIHNyYywKICAgICAgICBpbnRlcmFjdGl2ZVNjcmlwdCwgY3VycmVudGx5QWRkaW5nU2NyaXB0LCBtYWluU2NyaXB0LCBzdWJQYXRoLAogICAgICAgIHZlcnNpb24gPSAnMi4xLjIyJywKICAgICAgICBjb21tZW50UmVnRXhwID0gLyhcL1wqKFtcc1xTXSo/KVwqXC98KFteOl18XilcL1wvKC4qKSQpL21nLAogICAgICAgIGNqc1JlcXVpcmVSZWdFeHAgPSAvW14uXVxzKnJlcXVpcmVccypcKFxzKlsiJ10oW14nIlxzXSspWyInXVxzKlwpL2csCiAgICAgICAganNTdWZmaXhSZWdFeHAgPSAvXC5qcyQvLAogICAgICAgIGN1cnJEaXJSZWdFeHAgPSAvXlwuXC8vLAogICAgICAgIG9wID0gT2JqZWN0LnByb3RvdHlwZSwKICAgICAgICBvc3RyaW5nID0gb3AudG9TdHJpbmcsCiAgICAgICAgaGFzT3duID0gb3AuaGFzT3duUHJvcGVydHksCiAgICAgICAgYXAgPSBBcnJheS5wcm90b3R5cGUsCiAgICAgICAgaXNCcm93c2VyID0gISEodHlwZW9mIHdpbmRvdyAhPT0gJ3VuZGVmaW5lZCcgJiYgdHlwZW9mIG5hdmlnYXRvciAhPT0gJ3VuZGVmaW5lZCcgJiYgd2luZG93LmRvY3VtZW50KSwKICAgICAgICBpc1dlYldvcmtlciA9ICFpc0Jyb3dzZXIgJiYgdHlwZW9mIGltcG9ydFNjcmlwdHMgIT09ICd1bmRlZmluZWQnLAogICAgICAgIC8vUFMzIGluZGljYXRlcyBsb2FkZWQgYW5kIGNvbXBsZXRlLCBidXQgbmVlZCB0byB3YWl0IGZvciBjb21wbGV0ZQogICAgICAgIC8vc3BlY2lmaWNhbGx5LiBTZXF1ZW5jZSBpcyAnbG9hZGluZycsICdsb2FkZWQnLCBleGVjdXRpb24sCiAgICAgICAgLy8gdGhlbiAnY29tcGxldGUnLiBUaGUgVUEgY2hlY2sgaXMgdW5mb3J0dW5hdGUsIGJ1dCBub3Qgc3VyZSBob3cKICAgICAgICAvL3RvIGZlYXR1cmUgdGVzdCB3L28gY2F1c2luZyBwZXJmIGlzc3Vlcy4KICAgICAgICByZWFkeVJlZ0V4cCA9IGlzQnJvd3NlciAmJiBuYXZpZ2F0b3IucGxhdGZvcm0gPT09ICdQTEFZU1RBVElPTiAzJyA/CiAgICAgICAgICAgICAgICAgICAgICAvXmNvbXBsZXRlJC8gOiAvXihjb21wbGV0ZXxsb2FkZWQpJC8sCiAgICAgICAgZGVmQ29udGV4dE5hbWUgPSAnXycsCiAgICAgICAgLy9PaCB0aGUgdHJhZ2VkeSwgZGV0ZWN0aW5nIG9wZXJhLiBTZWUgdGhlIHVzYWdlIG9mIGlzT3BlcmEgZm9yIHJlYXNvbi4KICAgICAgICBpc09wZXJhID0gdHlwZW9mIG9wZXJhICE9PSAndW5kZWZpbmVkJyAmJiBvcGVyYS50b1N0cmluZygpID09PSAnW29iamVjdCBPcGVyYV0nLAogICAgICAgIGNvbnRleHRzID0ge30sCiAgICAgICAgY2ZnID0ge30sCiAgICAgICAgZ2xvYmFsRGVmUXVldWUgPSBbXSwKICAgICAgICB1c2VJbnRlcmFjdGl2ZSA9IGZhbHNlOwoKICAgIGZ1bmN0aW9uIGlzRnVuY3Rpb24oaXQpIHsKICAgICAgICByZXR1cm4gb3N0cmluZy5jYWxsKGl0KSA9PT0gJ1tvYmplY3QgRnVuY3Rpb25dJzsKICAgIH0KCiAgICBmdW5jdGlvbiBpc0FycmF5KGl0KSB7CiAgICAgICAgcmV0dXJuIG9zdHJpbmcuY2FsbChpdCkgPT09ICdbb2JqZWN0IEFycmF5XSc7CiAgICB9CgogICAgLyoqCiAgICAgKiBIZWxwZXIgZnVuY3Rpb24gZm9yIGl0ZXJhdGluZyBvdmVyIGFuIGFycmF5LiBJZiB0aGUgZnVuYyByZXR1cm5zCiAgICAgKiBhIHRydWUgdmFsdWUsIGl0IHdpbGwgYnJlYWsgb3V0IG9mIHRoZSBsb29wLgogICAgICovCiAgICBmdW5jdGlvbiBlYWNoKGFyeSwgZnVuYykgewogICAgICAgIGlmIChhcnkpIHsKICAgICAgICAgICAgdmFyIGk7CiAgICAgICAgICAgIGZvciAoaSA9IDA7IGkgPCBhcnkubGVuZ3RoOyBpICs9IDEpIHsKICAgICAgICAgICAgICAgIGlmIChhcnlbaV0gJiYgZnVuYyhhcnlbaV0sIGksIGFyeSkpIHsKICAgICAgICAgICAgICAgICAgICBicmVhazsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgIH0KICAgIH0KCiAgICAvKioKICAgICAqIEhlbHBlciBmdW5jdGlvbiBmb3IgaXRlcmF0aW5nIG92ZXIgYW4gYXJyYXkgYmFja3dhcmRzLiBJZiB0aGUgZnVuYwogICAgICogcmV0dXJucyBhIHRydWUgdmFsdWUsIGl0IHdpbGwgYnJlYWsgb3V0IG9mIHRoZSBsb29wLgogICAgICovCiAgICBmdW5jdGlvbiBlYWNoUmV2ZXJzZShhcnksIGZ1bmMpIHsKICAgICAgICBpZiAoYXJ5KSB7CiAgICAgICAgICAgIHZhciBpOwogICAgICAgICAgICBmb3IgKGkgPSBhcnkubGVuZ3RoIC0gMTsgaSA+IC0xOyBpIC09IDEpIHsKICAgICAgICAgICAgICAgIGlmIChhcnlbaV0gJiYgZnVuYyhhcnlbaV0sIGksIGFyeSkpIHsKICAgICAgICAgICAgICAgICAgICBicmVhazsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgIH0KICAgIH0KCiAgICBmdW5jdGlvbiBoYXNQcm9wKG9iaiwgcHJvcCkgewogICAgICAgIHJldHVybiBoYXNPd24uY2FsbChvYmosIHByb3ApOwogICAgfQoKICAgIGZ1bmN0aW9uIGdldE93bihvYmosIHByb3ApIHsKICAgICAgICByZXR1cm4gaGFzUHJvcChvYmosIHByb3ApICYmIG9ialtwcm9wXTsKICAgIH0KCiAgICAvKioKICAgICAqIEN5Y2xlcyBvdmVyIHByb3BlcnRpZXMgaW4gYW4gb2JqZWN0IGFuZCBjYWxscyBhIGZ1bmN0aW9uIGZvciBlYWNoCiAgICAgKiBwcm9wZXJ0eSB2YWx1ZS4gSWYgdGhlIGZ1bmN0aW9uIHJldHVybnMgYSB0cnV0aHkgdmFsdWUsIHRoZW4gdGhlCiAgICAgKiBpdGVyYXRpb24gaXMgc3RvcHBlZC4KICAgICAqLwogICAgZnVuY3Rpb24gZWFjaFByb3Aob2JqLCBmdW5jKSB7CiAgICAgICAgdmFyIHByb3A7CiAgICAgICAgZm9yIChwcm9wIGluIG9iaikgewogICAgICAgICAgICBpZiAoaGFzUHJvcChvYmosIHByb3ApKSB7CiAgICAgICAgICAgICAgICBpZiAoZnVuYyhvYmpbcHJvcF0sIHByb3ApKSB7CiAgICAgICAgICAgICAgICAgICAgYnJlYWs7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICB9CgogICAgLyoqCiAgICAgKiBTaW1wbGUgZnVuY3Rpb24gdG8gbWl4IGluIHByb3BlcnRpZXMgZnJvbSBzb3VyY2UgaW50byB0YXJnZXQsCiAgICAgKiBidXQgb25seSBpZiB0YXJnZXQgZG9lcyBub3QgYWxyZWFkeSBoYXZlIGEgcHJvcGVydHkgb2YgdGhlIHNhbWUgbmFtZS4KICAgICAqLwogICAgZnVuY3Rpb24gbWl4aW4odGFyZ2V0LCBzb3VyY2UsIGZvcmNlLCBkZWVwU3RyaW5nTWl4aW4pIHsKICAgICAgICBpZiAoc291cmNlKSB7CiAgICAgICAgICAgIGVhY2hQcm9wKHNvdXJjZSwgZnVuY3Rpb24gKHZhbHVlLCBwcm9wKSB7CiAgICAgICAgICAgICAgICBpZiAoZm9yY2UgfHwgIWhhc1Byb3AodGFyZ2V0LCBwcm9wKSkgewogICAgICAgICAgICAgICAgICAgIGlmIChkZWVwU3RyaW5nTWl4aW4gJiYgdHlwZW9mIHZhbHVlID09PSAnb2JqZWN0JyAmJiB2YWx1ZSAmJgogICAgICAgICAgICAgICAgICAgICAgICAhaXNBcnJheSh2YWx1ZSkgJiYgIWlzRnVuY3Rpb24odmFsdWUpICYmCiAgICAgICAgICAgICAgICAgICAgICAgICEodmFsdWUgaW5zdGFuY2VvZiBSZWdFeHApKSB7CgogICAgICAgICAgICAgICAgICAgICAgICBpZiAoIXRhcmdldFtwcm9wXSkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgdGFyZ2V0W3Byb3BdID0ge307CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgbWl4aW4odGFyZ2V0W3Byb3BdLCB2YWx1ZSwgZm9yY2UsIGRlZXBTdHJpbmdNaXhpbik7CiAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgICAgICAgICAgICAgdGFyZ2V0W3Byb3BdID0gdmFsdWU7CiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9KTsKICAgICAgICB9CiAgICAgICAgcmV0dXJuIHRhcmdldDsKICAgIH0KCiAgICAvL1NpbWlsYXIgdG8gRnVuY3Rpb24ucHJvdG90eXBlLmJpbmQsIGJ1dCB0aGUgJ3RoaXMnIG9iamVjdCBpcyBzcGVjaWZpZWQKICAgIC8vZmlyc3QsIHNpbmNlIGl0IGlzIGVhc2llciB0byByZWFkL2ZpZ3VyZSBvdXQgd2hhdCAndGhpcycgd2lsbCBiZS4KICAgIGZ1bmN0aW9uIGJpbmQob2JqLCBmbikgewogICAgICAgIHJldHVybiBmdW5jdGlvbiAoKSB7CiAgICAgICAgICAgIHJldHVybiBmbi5hcHBseShvYmosIGFyZ3VtZW50cyk7CiAgICAgICAgfTsKICAgIH0KCiAgICBmdW5jdGlvbiBzY3JpcHRzKCkgewogICAgICAgIHJldHVybiBkb2N1bWVudC5nZXRFbGVtZW50c0J5VGFnTmFtZSgnc2NyaXB0Jyk7CiAgICB9CgogICAgZnVuY3Rpb24gZGVmYXVsdE9uRXJyb3IoZXJyKSB7CiAgICAgICAgdGhyb3cgZXJyOwogICAgfQoKICAgIC8vQWxsb3cgZ2V0dGluZyBhIGdsb2JhbCB0aGF0IGlzIGV4cHJlc3NlZCBpbgogICAgLy9kb3Qgbm90YXRpb24sIGxpa2UgJ2EuYi5jJy4KICAgIGZ1bmN0aW9uIGdldEdsb2JhbCh2YWx1ZSkgewogICAgICAgIGlmICghdmFsdWUpIHsKICAgICAgICAgICAgcmV0dXJuIHZhbHVlOwogICAgICAgIH0KICAgICAgICB2YXIgZyA9IGdsb2JhbDsKICAgICAgICBlYWNoKHZhbHVlLnNwbGl0KCcuJyksIGZ1bmN0aW9uIChwYXJ0KSB7CiAgICAgICAgICAgIGcgPSBnW3BhcnRdOwogICAgICAgIH0pOwogICAgICAgIHJldHVybiBnOwogICAgfQoKICAgIC8qKgogICAgICogQ29uc3RydWN0cyBhbiBlcnJvciB3aXRoIGEgcG9pbnRlciB0byBhbiBVUkwgd2l0aCBtb3JlIGluZm9ybWF0aW9uLgogICAgICogQHBhcmFtIHtTdHJpbmd9IGlkIHRoZSBlcnJvciBJRCB0aGF0IG1hcHMgdG8gYW4gSUQgb24gYSB3ZWIgcGFnZS4KICAgICAqIEBwYXJhbSB7U3RyaW5nfSBtZXNzYWdlIGh1bWFuIHJlYWRhYmxlIGVycm9yLgogICAgICogQHBhcmFtIHtFcnJvcn0gW2Vycl0gdGhlIG9yaWdpbmFsIGVycm9yLCBpZiB0aGVyZSBpcyBvbmUuCiAgICAgKgogICAgICogQHJldHVybnMge0Vycm9yfQogICAgICovCiAgICBmdW5jdGlvbiBtYWtlRXJyb3IoaWQsIG1zZywgZXJyLCByZXF1aXJlTW9kdWxlcykgewogICAgICAgIHZhciBlID0gbmV3IEVycm9yKG1zZyArICdcbmh0dHA6Ly9yZXF1aXJlanMub3JnL2RvY3MvZXJyb3JzLmh0bWwjJyArIGlkKTsKICAgICAgICBlLnJlcXVpcmVUeXBlID0gaWQ7CiAgICAgICAgZS5yZXF1aXJlTW9kdWxlcyA9IHJlcXVpcmVNb2R1bGVzOwogICAgICAgIGlmIChlcnIpIHsKICAgICAgICAgICAgZS5vcmlnaW5hbEVycm9yID0gZXJyOwogICAgICAgIH0KICAgICAgICByZXR1cm4gZTsKICAgIH0KCiAgICBpZiAodHlwZW9mIGRlZmluZSAhPT0gJ3VuZGVmaW5lZCcpIHsKICAgICAgICAvL0lmIGEgZGVmaW5lIGlzIGFscmVhZHkgaW4gcGxheSB2aWEgYW5vdGhlciBBTUQgbG9hZGVyLAogICAgICAgIC8vZG8gbm90IG92ZXJ3cml0ZS4KICAgICAgICByZXR1cm47CiAgICB9CgogICAgaWYgKHR5cGVvZiByZXF1aXJlanMgIT09ICd1bmRlZmluZWQnKSB7CiAgICAgICAgaWYgKGlzRnVuY3Rpb24ocmVxdWlyZWpzKSkgewogICAgICAgICAgICAvL0RvIG5vdCBvdmVyd3JpdGUgYW4gZXhpc3RpbmcgcmVxdWlyZWpzIGluc3RhbmNlLgogICAgICAgICAgICByZXR1cm47CiAgICAgICAgfQogICAgICAgIGNmZyA9IHJlcXVpcmVqczsKICAgICAgICByZXF1aXJlanMgPSB1bmRlZmluZWQ7CiAgICB9CgogICAgLy9BbGxvdyBmb3IgYSByZXF1aXJlIGNvbmZpZyBvYmplY3QKICAgIGlmICh0eXBlb2YgcmVxdWlyZSAhPT0gJ3VuZGVmaW5lZCcgJiYgIWlzRnVuY3Rpb24ocmVxdWlyZSkpIHsKICAgICAgICAvL2Fzc3VtZSBpdCBpcyBhIGNvbmZpZyBvYmplY3QuCiAgICAgICAgY2ZnID0gcmVxdWlyZTsKICAgICAgICByZXF1aXJlID0gdW5kZWZpbmVkOwogICAgfQoKICAgIGZ1bmN0aW9uIG5ld0NvbnRleHQoY29udGV4dE5hbWUpIHsKICAgICAgICB2YXIgaW5DaGVja0xvYWRlZCwgTW9kdWxlLCBjb250ZXh0LCBoYW5kbGVycywKICAgICAgICAgICAgY2hlY2tMb2FkZWRUaW1lb3V0SWQsCiAgICAgICAgICAgIGNvbmZpZyA9IHsKICAgICAgICAgICAgICAgIC8vRGVmYXVsdHMuIERvIG5vdCBzZXQgYSBkZWZhdWx0IGZvciBtYXAKICAgICAgICAgICAgICAgIC8vY29uZmlnIHRvIHNwZWVkIHVwIG5vcm1hbGl6ZSgpLCB3aGljaAogICAgICAgICAgICAgICAgLy93aWxsIHJ1biBmYXN0ZXIgaWYgdGhlcmUgaXMgbm8gZGVmYXVsdC4KICAgICAgICAgICAgICAgIHdhaXRTZWNvbmRzOiA3LAogICAgICAgICAgICAgICAgYmFzZVVybDogJy4vJywKICAgICAgICAgICAgICAgIHBhdGhzOiB7fSwKICAgICAgICAgICAgICAgIGJ1bmRsZXM6IHt9LAogICAgICAgICAgICAgICAgcGtnczoge30sCiAgICAgICAgICAgICAgICBzaGltOiB7fSwKICAgICAgICAgICAgICAgIGNvbmZpZzoge30KICAgICAgICAgICAgfSwKICAgICAgICAgICAgcmVnaXN0cnkgPSB7fSwKICAgICAgICAgICAgLy9yZWdpc3RyeSBvZiBqdXN0IGVuYWJsZWQgbW9kdWxlcywgdG8gc3BlZWQKICAgICAgICAgICAgLy9jeWNsZSBicmVha2luZyBjb2RlIHdoZW4gbG90cyBvZiBtb2R1bGVzCiAgICAgICAgICAgIC8vYXJlIHJlZ2lzdGVyZWQsIGJ1dCBub3QgYWN0aXZhdGVkLgogICAgICAgICAgICBlbmFibGVkUmVnaXN0cnkgPSB7fSwKICAgICAgICAgICAgdW5kZWZFdmVudHMgPSB7fSwKICAgICAgICAgICAgZGVmUXVldWUgPSBbXSwKICAgICAgICAgICAgZGVmaW5lZCA9IHt9LAogICAgICAgICAgICB1cmxGZXRjaGVkID0ge30sCiAgICAgICAgICAgIGJ1bmRsZXNNYXAgPSB7fSwKICAgICAgICAgICAgcmVxdWlyZUNvdW50ZXIgPSAxLAogICAgICAgICAgICB1bm5vcm1hbGl6ZWRDb3VudGVyID0gMTsKCiAgICAgICAgLyoqCiAgICAgICAgICogVHJpbXMgdGhlIC4gYW5kIC4uIGZyb20gYW4gYXJyYXkgb2YgcGF0aCBzZWdtZW50cy4KICAgICAgICAgKiBJdCB3aWxsIGtlZXAgYSBsZWFkaW5nIHBhdGggc2VnbWVudCBpZiBhIC4uIHdpbGwgYmVjb21lCiAgICAgICAgICogdGhlIGZpcnN0IHBhdGggc2VnbWVudCwgdG8gaGVscCB3aXRoIG1vZHVsZSBuYW1lIGxvb2t1cHMsCiAgICAgICAgICogd2hpY2ggYWN0IGxpa2UgcGF0aHMsIGJ1dCBjYW4gYmUgcmVtYXBwZWQuIEJ1dCB0aGUgZW5kIHJlc3VsdCwKICAgICAgICAgKiBhbGwgcGF0aHMgdGhhdCB1c2UgdGhpcyBmdW5jdGlvbiBzaG91bGQgbG9vayBub3JtYWxpemVkLgogICAgICAgICAqIE5PVEU6IHRoaXMgbWV0aG9kIE1PRElGSUVTIHRoZSBpbnB1dCBhcnJheS4KICAgICAgICAgKiBAcGFyYW0ge0FycmF5fSBhcnkgdGhlIGFycmF5IG9mIHBhdGggc2VnbWVudHMuCiAgICAgICAgICovCiAgICAgICAgZnVuY3Rpb24gdHJpbURvdHMoYXJ5KSB7CiAgICAgICAgICAgIHZhciBpLCBwYXJ0OwogICAgICAgICAgICBmb3IgKGkgPSAwOyBpIDwgYXJ5Lmxlbmd0aDsgaSsrKSB7CiAgICAgICAgICAgICAgICBwYXJ0ID0gYXJ5W2ldOwogICAgICAgICAgICAgICAgaWYgKHBhcnQgPT09ICcuJykgewogICAgICAgICAgICAgICAgICAgIGFyeS5zcGxpY2UoaSwgMSk7CiAgICAgICAgICAgICAgICAgICAgaSAtPSAxOwogICAgICAgICAgICAgICAgfSBlbHNlIGlmIChwYXJ0ID09PSAnLi4nKSB7CiAgICAgICAgICAgICAgICAgICAgLy8gSWYgYXQgdGhlIHN0YXJ0LCBvciBwcmV2aW91cyB2YWx1ZSBpcyBzdGlsbCAuLiwKICAgICAgICAgICAgICAgICAgICAvLyBrZWVwIHRoZW0gc28gdGhhdCB3aGVuIGNvbnZlcnRlZCB0byBhIHBhdGggaXQgbWF5CiAgICAgICAgICAgICAgICAgICAgLy8gc3RpbGwgd29yayB3aGVuIGNvbnZlcnRlZCB0byBhIHBhdGgsIGV2ZW4gdGhvdWdoCiAgICAgICAgICAgICAgICAgICAgLy8gYXMgYW4gSUQgaXQgaXMgbGVzcyB0aGFuIGlkZWFsLiBJbiBsYXJnZXIgcG9pbnQKICAgICAgICAgICAgICAgICAgICAvLyByZWxlYXNlcywgbWF5IGJlIGJldHRlciB0byBqdXN0IGtpY2sgb3V0IGFuIGVycm9yLgogICAgICAgICAgICAgICAgICAgIGlmIChpID09PSAwIHx8IChpID09PSAxICYmIGFyeVsyXSA9PT0gJy4uJykgfHwgYXJ5W2kgLSAxXSA9PT0gJy4uJykgewogICAgICAgICAgICAgICAgICAgICAgICBjb250aW51ZTsKICAgICAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKGkgPiAwKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIGFyeS5zcGxpY2UoaSAtIDEsIDIpOwogICAgICAgICAgICAgICAgICAgICAgICBpIC09IDI7CiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CiAgICAgICAgfQoKICAgICAgICAvKioKICAgICAgICAgKiBHaXZlbiBhIHJlbGF0aXZlIG1vZHVsZSBuYW1lLCBsaWtlIC4vc29tZXRoaW5nLCBub3JtYWxpemUgaXQgdG8KICAgICAgICAgKiBhIHJlYWwgbmFtZSB0aGF0IGNhbiBiZSBtYXBwZWQgdG8gYSBwYXRoLgogICAgICAgICAqIEBwYXJhbSB7U3RyaW5nfSBuYW1lIHRoZSByZWxhdGl2ZSBuYW1lCiAgICAgICAgICogQHBhcmFtIHtTdHJpbmd9IGJhc2VOYW1lIGEgcmVhbCBuYW1lIHRoYXQgdGhlIG5hbWUgYXJnIGlzIHJlbGF0aXZlCiAgICAgICAgICogdG8uCiAgICAgICAgICogQHBhcmFtIHtCb29sZWFufSBhcHBseU1hcCBhcHBseSB0aGUgbWFwIGNvbmZpZyB0byB0aGUgdmFsdWUuIFNob3VsZAogICAgICAgICAqIG9ubHkgYmUgZG9uZSBpZiB0aGlzIG5vcm1hbGl6YXRpb24gaXMgZm9yIGEgZGVwZW5kZW5jeSBJRC4KICAgICAgICAgKiBAcmV0dXJucyB7U3RyaW5nfSBub3JtYWxpemVkIG5hbWUKICAgICAgICAgKi8KICAgICAgICBmdW5jdGlvbiBub3JtYWxpemUobmFtZSwgYmFzZU5hbWUsIGFwcGx5TWFwKSB7CiAgICAgICAgICAgIHZhciBwa2dNYWluLCBtYXBWYWx1ZSwgbmFtZVBhcnRzLCBpLCBqLCBuYW1lU2VnbWVudCwgbGFzdEluZGV4LAogICAgICAgICAgICAgICAgZm91bmRNYXAsIGZvdW5kSSwgZm91bmRTdGFyTWFwLCBzdGFySSwgbm9ybWFsaXplZEJhc2VQYXJ0cywKICAgICAgICAgICAgICAgIGJhc2VQYXJ0cyA9IChiYXNlTmFtZSAmJiBiYXNlTmFtZS5zcGxpdCgnLycpKSwKICAgICAgICAgICAgICAgIG1hcCA9IGNvbmZpZy5tYXAsCiAgICAgICAgICAgICAgICBzdGFyTWFwID0gbWFwICYmIG1hcFsnKiddOwoKICAgICAgICAgICAgLy9BZGp1c3QgYW55IHJlbGF0aXZlIHBhdGhzLgogICAgICAgICAgICBpZiAobmFtZSkgewogICAgICAgICAgICAgICAgbmFtZSA9IG5hbWUuc3BsaXQoJy8nKTsKICAgICAgICAgICAgICAgIGxhc3RJbmRleCA9IG5hbWUubGVuZ3RoIC0gMTsKCiAgICAgICAgICAgICAgICAvLyBJZiB3YW50aW5nIG5vZGUgSUQgY29tcGF0aWJpbGl0eSwgc3RyaXAgLmpzIGZyb20gZW5kCiAgICAgICAgICAgICAgICAvLyBvZiBJRHMuIEhhdmUgdG8gZG8gdGhpcyBoZXJlLCBhbmQgbm90IGluIG5hbWVUb1VybAogICAgICAgICAgICAgICAgLy8gYmVjYXVzZSBub2RlIGFsbG93cyBlaXRoZXIgLmpzIG9yIG5vbiAuanMgdG8gbWFwCiAgICAgICAgICAgICAgICAvLyB0byBzYW1lIGZpbGUuCiAgICAgICAgICAgICAgICBpZiAoY29uZmlnLm5vZGVJZENvbXBhdCAmJiBqc1N1ZmZpeFJlZ0V4cC50ZXN0KG5hbWVbbGFzdEluZGV4XSkpIHsKICAgICAgICAgICAgICAgICAgICBuYW1lW2xhc3RJbmRleF0gPSBuYW1lW2xhc3RJbmRleF0ucmVwbGFjZShqc1N1ZmZpeFJlZ0V4cCwgJycpOwogICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgIC8vIFN0YXJ0cyB3aXRoIGEgJy4nIHNvIG5lZWQgdGhlIGJhc2VOYW1lCiAgICAgICAgICAgICAgICBpZiAobmFtZVswXS5jaGFyQXQoMCkgPT09ICcuJyAmJiBiYXNlUGFydHMpIHsKICAgICAgICAgICAgICAgICAgICAvL0NvbnZlcnQgYmFzZU5hbWUgdG8gYXJyYXksIGFuZCBsb3Agb2ZmIHRoZSBsYXN0IHBhcnQsCiAgICAgICAgICAgICAgICAgICAgLy9zbyB0aGF0IC4gbWF0Y2hlcyB0aGF0ICdkaXJlY3RvcnknIGFuZCBub3QgbmFtZSBvZiB0aGUgYmFzZU5hbWUncwogICAgICAgICAgICAgICAgICAgIC8vbW9kdWxlLiBGb3IgaW5zdGFuY2UsIGJhc2VOYW1lIG9mICdvbmUvdHdvL3RocmVlJywgbWFwcyB0bwogICAgICAgICAgICAgICAgICAgIC8vJ29uZS90d28vdGhyZWUuanMnLCBidXQgd2Ugd2FudCB0aGUgZGlyZWN0b3J5LCAnb25lL3R3bycgZm9yCiAgICAgICAgICAgICAgICAgICAgLy90aGlzIG5vcm1hbGl6YXRpb24uCiAgICAgICAgICAgICAgICAgICAgbm9ybWFsaXplZEJhc2VQYXJ0cyA9IGJhc2VQYXJ0cy5zbGljZSgwLCBiYXNlUGFydHMubGVuZ3RoIC0gMSk7CiAgICAgICAgICAgICAgICAgICAgbmFtZSA9IG5vcm1hbGl6ZWRCYXNlUGFydHMuY29uY2F0KG5hbWUpOwogICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgIHRyaW1Eb3RzKG5hbWUpOwogICAgICAgICAgICAgICAgbmFtZSA9IG5hbWUuam9pbignLycpOwogICAgICAgICAgICB9CgogICAgICAgICAgICAvL0FwcGx5IG1hcCBjb25maWcgaWYgYXZhaWxhYmxlLgogICAgICAgICAgICBpZiAoYXBwbHlNYXAgJiYgbWFwICYmIChiYXNlUGFydHMgfHwgc3Rhck1hcCkpIHsKICAgICAgICAgICAgICAgIG5hbWVQYXJ0cyA9IG5hbWUuc3BsaXQoJy8nKTsKCiAgICAgICAgICAgICAgICBvdXRlckxvb3A6IGZvciAoaSA9IG5hbWVQYXJ0cy5sZW5ndGg7IGkgPiAwOyBpIC09IDEpIHsKICAgICAgICAgICAgICAgICAgICBuYW1lU2VnbWVudCA9IG5hbWVQYXJ0cy5zbGljZSgwLCBpKS5qb2luKCcvJyk7CgogICAgICAgICAgICAgICAgICAgIGlmIChiYXNlUGFydHMpIHsKICAgICAgICAgICAgICAgICAgICAgICAgLy9GaW5kIHRoZSBsb25nZXN0IGJhc2VOYW1lIHNlZ21lbnQgbWF0Y2ggaW4gdGhlIGNvbmZpZy4KICAgICAgICAgICAgICAgICAgICAgICAgLy9TbywgZG8gam9pbnMgb24gdGhlIGJpZ2dlc3QgdG8gc21hbGxlc3QgbGVuZ3RocyBvZiBiYXNlUGFydHMuCiAgICAgICAgICAgICAgICAgICAgICAgIGZvciAoaiA9IGJhc2VQYXJ0cy5sZW5ndGg7IGogPiAwOyBqIC09IDEpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1hcFZhbHVlID0gZ2V0T3duKG1hcCwgYmFzZVBhcnRzLnNsaWNlKDAsIGopLmpvaW4oJy8nKSk7CgogICAgICAgICAgICAgICAgICAgICAgICAgICAgLy9iYXNlTmFtZSBzZWdtZW50IGhhcyBjb25maWcsIGZpbmQgaWYgaXQgaGFzIG9uZSBmb3IKICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vdGhpcyBuYW1lLgogICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKG1hcFZhbHVlKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWFwVmFsdWUgPSBnZXRPd24obWFwVmFsdWUsIG5hbWVTZWdtZW50KTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAobWFwVmFsdWUpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy9NYXRjaCwgdXBkYXRlIG5hbWUgdG8gdGhlIG5ldyB2YWx1ZS4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZm91bmRNYXAgPSBtYXBWYWx1ZTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZm91bmRJID0gaTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWsgb3V0ZXJMb29wOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICAgICAgLy9DaGVjayBmb3IgYSBzdGFyIG1hcCBtYXRjaCwgYnV0IGp1c3QgaG9sZCBvbiB0byBpdCwKICAgICAgICAgICAgICAgICAgICAvL2lmIHRoZXJlIGlzIGEgc2hvcnRlciBzZWdtZW50IG1hdGNoIGxhdGVyIGluIGEgbWF0Y2hpbmcKICAgICAgICAgICAgICAgICAgICAvL2NvbmZpZywgdGhlbiBmYXZvciBvdmVyIHRoaXMgc3RhciBtYXAuCiAgICAgICAgICAgICAgICAgICAgaWYgKCFmb3VuZFN0YXJNYXAgJiYgc3Rhck1hcCAmJiBnZXRPd24oc3Rhck1hcCwgbmFtZVNlZ21lbnQpKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIGZvdW5kU3Rhck1hcCA9IGdldE93bihzdGFyTWFwLCBuYW1lU2VnbWVudCk7CiAgICAgICAgICAgICAgICAgICAgICAgIHN0YXJJID0gaTsKICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgaWYgKCFmb3VuZE1hcCAmJiBmb3VuZFN0YXJNYXApIHsKICAgICAgICAgICAgICAgICAgICBmb3VuZE1hcCA9IGZvdW5kU3Rhck1hcDsKICAgICAgICAgICAgICAgICAgICBmb3VuZEkgPSBzdGFySTsKICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICBpZiAoZm91bmRNYXApIHsKICAgICAgICAgICAgICAgICAgICBuYW1lUGFydHMuc3BsaWNlKDAsIGZvdW5kSSwgZm91bmRNYXApOwogICAgICAgICAgICAgICAgICAgIG5hbWUgPSBuYW1lUGFydHMuam9pbignLycpOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CgogICAgICAgICAgICAvLyBJZiB0aGUgbmFtZSBwb2ludHMgdG8gYSBwYWNrYWdlJ3MgbmFtZSwgdXNlCiAgICAgICAgICAgIC8vIHRoZSBwYWNrYWdlIG1haW4gaW5zdGVhZC4KICAgICAgICAgICAgcGtnTWFpbiA9IGdldE93bihjb25maWcucGtncywgbmFtZSk7CgogICAgICAgICAgICByZXR1cm4gcGtnTWFpbiA/IHBrZ01haW4gOiBuYW1lOwogICAgICAgIH0KCiAgICAgICAgZnVuY3Rpb24gcmVtb3ZlU2NyaXB0KG5hbWUpIHsKICAgICAgICAgICAgaWYgKGlzQnJvd3NlcikgewogICAgICAgICAgICAgICAgZWFjaChzY3JpcHRzKCksIGZ1bmN0aW9uIChzY3JpcHROb2RlKSB7CiAgICAgICAgICAgICAgICAgICAgaWYgKHNjcmlwdE5vZGUuZ2V0QXR0cmlidXRlKCdkYXRhLXJlcXVpcmVtb2R1bGUnKSA9PT0gbmFtZSAmJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgc2NyaXB0Tm9kZS5nZXRBdHRyaWJ1dGUoJ2RhdGEtcmVxdWlyZWNvbnRleHQnKSA9PT0gY29udGV4dC5jb250ZXh0TmFtZSkgewogICAgICAgICAgICAgICAgICAgICAgICBzY3JpcHROb2RlLnBhcmVudE5vZGUucmVtb3ZlQ2hpbGQoc2NyaXB0Tm9kZSk7CiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiB0cnVlOwogICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIH0pOwogICAgICAgICAgICB9CiAgICAgICAgfQoKICAgICAgICBmdW5jdGlvbiBoYXNQYXRoRmFsbGJhY2soaWQpIHsKICAgICAgICAgICAgdmFyIHBhdGhDb25maWcgPSBnZXRPd24oY29uZmlnLnBhdGhzLCBpZCk7CiAgICAgICAgICAgIGlmIChwYXRoQ29uZmlnICYmIGlzQXJyYXkocGF0aENvbmZpZykgJiYgcGF0aENvbmZpZy5sZW5ndGggPiAxKSB7CiAgICAgICAgICAgICAgICAvL1BvcCBvZmYgdGhlIGZpcnN0IGFycmF5IHZhbHVlLCBzaW5jZSBpdCBmYWlsZWQsIGFuZAogICAgICAgICAgICAgICAgLy9yZXRyeQogICAgICAgICAgICAgICAgcGF0aENvbmZpZy5zaGlmdCgpOwogICAgICAgICAgICAgICAgY29udGV4dC5yZXF1aXJlLnVuZGVmKGlkKTsKCiAgICAgICAgICAgICAgICAvL0N1c3RvbSByZXF1aXJlIHRoYXQgZG9lcyBub3QgZG8gbWFwIHRyYW5zbGF0aW9uLCBzaW5jZQogICAgICAgICAgICAgICAgLy9JRCBpcyAiYWJzb2x1dGUiLCBhbHJlYWR5IG1hcHBlZC9yZXNvbHZlZC4KICAgICAgICAgICAgICAgIGNvbnRleHQubWFrZVJlcXVpcmUobnVsbCwgewogICAgICAgICAgICAgICAgICAgIHNraXBNYXA6IHRydWUKICAgICAgICAgICAgICAgIH0pKFtpZF0pOwoKICAgICAgICAgICAgICAgIHJldHVybiB0cnVlOwogICAgICAgICAgICB9CiAgICAgICAgfQoKICAgICAgICAvL1R1cm5zIGEgcGx1Z2luIXJlc291cmNlIHRvIFtwbHVnaW4sIHJlc291cmNlXQogICAgICAgIC8vd2l0aCB0aGUgcGx1Z2luIGJlaW5nIHVuZGVmaW5lZCBpZiB0aGUgbmFtZQogICAgICAgIC8vZGlkIG5vdCBoYXZlIGEgcGx1Z2luIHByZWZpeC4KICAgICAgICBmdW5jdGlvbiBzcGxpdFByZWZpeChuYW1lKSB7CiAgICAgICAgICAgIHZhciBwcmVmaXgsCiAgICAgICAgICAgICAgICBpbmRleCA9IG5hbWUgPyBuYW1lLmluZGV4T2YoJyEnKSA6IC0xOwogICAgICAgICAgICBpZiAoaW5kZXggPiAtMSkgewogICAgICAgICAgICAgICAgcHJlZml4ID0gbmFtZS5zdWJzdHJpbmcoMCwgaW5kZXgpOwogICAgICAgICAgICAgICAgbmFtZSA9IG5hbWUuc3Vic3RyaW5nKGluZGV4ICsgMSwgbmFtZS5sZW5ndGgpOwogICAgICAgICAgICB9CiAgICAgICAgICAgIHJldHVybiBbcHJlZml4LCBuYW1lXTsKICAgICAgICB9CgogICAgICAgIC8qKgogICAgICAgICAqIENyZWF0ZXMgYSBtb2R1bGUgbWFwcGluZyB0aGF0IGluY2x1ZGVzIHBsdWdpbiBwcmVmaXgsIG1vZHVsZQogICAgICAgICAqIG5hbWUsIGFuZCBwYXRoLiBJZiBwYXJlbnRNb2R1bGVNYXAgaXMgcHJvdmlkZWQgaXQgd2lsbAogICAgICAgICAqIGFsc28gbm9ybWFsaXplIHRoZSBuYW1lIHZpYSByZXF1aXJlLm5vcm1hbGl6ZSgpCiAgICAgICAgICoKICAgICAgICAgKiBAcGFyYW0ge1N0cmluZ30gbmFtZSB0aGUgbW9kdWxlIG5hbWUKICAgICAgICAgKiBAcGFyYW0ge1N0cmluZ30gW3BhcmVudE1vZHVsZU1hcF0gcGFyZW50IG1vZHVsZSBtYXAKICAgICAgICAgKiBmb3IgdGhlIG1vZHVsZSBuYW1lLCB1c2VkIHRvIHJlc29sdmUgcmVsYXRpdmUgbmFtZXMuCiAgICAgICAgICogQHBhcmFtIHtCb29sZWFufSBpc05vcm1hbGl6ZWQ6IGlzIHRoZSBJRCBhbHJlYWR5IG5vcm1hbGl6ZWQuCiAgICAgICAgICogVGhpcyBpcyB0cnVlIGlmIHRoaXMgY2FsbCBpcyBkb25lIGZvciBhIGRlZmluZSgpIG1vZHVsZSBJRC4KICAgICAgICAgKiBAcGFyYW0ge0Jvb2xlYW59IGFwcGx5TWFwOiBhcHBseSB0aGUgbWFwIGNvbmZpZyB0byB0aGUgSUQuCiAgICAgICAgICogU2hvdWxkIG9ubHkgYmUgdHJ1ZSBpZiB0aGlzIG1hcCBpcyBmb3IgYSBkZXBlbmRlbmN5LgogICAgICAgICAqCiAgICAgICAgICogQHJldHVybnMge09iamVjdH0KICAgICAgICAgKi8KICAgICAgICBmdW5jdGlvbiBtYWtlTW9kdWxlTWFwKG5hbWUsIHBhcmVudE1vZHVsZU1hcCwgaXNOb3JtYWxpemVkLCBhcHBseU1hcCkgewogICAgICAgICAgICB2YXIgdXJsLCBwbHVnaW5Nb2R1bGUsIHN1ZmZpeCwgbmFtZVBhcnRzLAogICAgICAgICAgICAgICAgcHJlZml4ID0gbnVsbCwKICAgICAgICAgICAgICAgIHBhcmVudE5hbWUgPSBwYXJlbnRNb2R1bGVNYXAgPyBwYXJlbnRNb2R1bGVNYXAubmFtZSA6IG51bGwsCiAgICAgICAgICAgICAgICBvcmlnaW5hbE5hbWUgPSBuYW1lLAogICAgICAgICAgICAgICAgaXNEZWZpbmUgPSB0cnVlLAogICAgICAgICAgICAgICAgbm9ybWFsaXplZE5hbWUgPSAnJzsKCiAgICAgICAgICAgIC8vSWYgbm8gbmFtZSwgdGhlbiBpdCBtZWFucyBpdCBpcyBhIHJlcXVpcmUgY2FsbCwgZ2VuZXJhdGUgYW4KICAgICAgICAgICAgLy9pbnRlcm5hbCBuYW1lLgogICAgICAgICAgICBpZiAoIW5hbWUpIHsKICAgICAgICAgICAgICAgIGlzRGVmaW5lID0gZmFsc2U7CiAgICAgICAgICAgICAgICBuYW1lID0gJ19AcicgKyAocmVxdWlyZUNvdW50ZXIgKz0gMSk7CiAgICAgICAgICAgIH0KCiAgICAgICAgICAgIG5hbWVQYXJ0cyA9IHNwbGl0UHJlZml4KG5hbWUpOwogICAgICAgICAgICBwcmVmaXggPSBuYW1lUGFydHNbMF07CiAgICAgICAgICAgIG5hbWUgPSBuYW1lUGFydHNbMV07CgogICAgICAgICAgICBpZiAocHJlZml4KSB7CiAgICAgICAgICAgICAgICBwcmVmaXggPSBub3JtYWxpemUocHJlZml4LCBwYXJlbnROYW1lLCBhcHBseU1hcCk7CiAgICAgICAgICAgICAgICBwbHVnaW5Nb2R1bGUgPSBnZXRPd24oZGVmaW5lZCwgcHJlZml4KTsKICAgICAgICAgICAgfQoKICAgICAgICAgICAgLy9BY2NvdW50IGZvciByZWxhdGl2ZSBwYXRocyBpZiB0aGVyZSBpcyBhIGJhc2UgbmFtZS4KICAgICAgICAgICAgaWYgKG5hbWUpIHsKICAgICAgICAgICAgICAgIGlmIChwcmVmaXgpIHsKICAgICAgICAgICAgICAgICAgICBpZiAocGx1Z2luTW9kdWxlICYmIHBsdWdpbk1vZHVsZS5ub3JtYWxpemUpIHsKICAgICAgICAgICAgICAgICAgICAgICAgLy9QbHVnaW4gaXMgbG9hZGVkLCB1c2UgaXRzIG5vcm1hbGl6ZSBtZXRob2QuCiAgICAgICAgICAgICAgICAgICAgICAgIG5vcm1hbGl6ZWROYW1lID0gcGx1Z2luTW9kdWxlLm5vcm1hbGl6ZShuYW1lLCBmdW5jdGlvbiAobmFtZSkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIG5vcm1hbGl6ZShuYW1lLCBwYXJlbnROYW1lLCBhcHBseU1hcCk7CiAgICAgICAgICAgICAgICAgICAgICAgIH0pOwogICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgICAgICAgICAgICAgIC8vIElmIG5lc3RlZCBwbHVnaW4gcmVmZXJlbmNlcywgdGhlbiBkbyBub3QgdHJ5IHRvCiAgICAgICAgICAgICAgICAgICAgICAgIC8vIG5vcm1hbGl6ZSwgYXMgaXQgd2lsbCBub3Qgbm9ybWFsaXplIGNvcnJlY3RseS4gVGhpcwogICAgICAgICAgICAgICAgICAgICAgICAvLyBwbGFjZXMgYSByZXN0cmljdGlvbiBvbiByZXNvdXJjZUlkcywgYW5kIHRoZSBsb25nZXIKICAgICAgICAgICAgICAgICAgICAgICAgLy8gdGVybSBzb2x1dGlvbiBpcyBub3QgdG8gbm9ybWFsaXplIHVudGlsIHBsdWdpbnMgYXJlCiAgICAgICAgICAgICAgICAgICAgICAgIC8vIGxvYWRlZCBhbmQgYWxsIG5vcm1hbGl6YXRpb25zIHRvIGFsbG93IGZvciBhc3luYwogICAgICAgICAgICAgICAgICAgICAgICAvLyBsb2FkaW5nIG9mIGEgbG9hZGVyIHBsdWdpbi4gQnV0IGZvciBub3csIGZpeGVzIHRoZQogICAgICAgICAgICAgICAgICAgICAgICAvLyBjb21tb24gdXNlcy4gRGV0YWlscyBpbiAjMTEzMQogICAgICAgICAgICAgICAgICAgICAgICBub3JtYWxpemVkTmFtZSA9IG5hbWUuaW5kZXhPZignIScpID09PSAtMSA/CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm9ybWFsaXplKG5hbWUsIHBhcmVudE5hbWUsIGFwcGx5TWFwKSA6CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmFtZTsKICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICAgICAgICAgIC8vQSByZWd1bGFyIG1vZHVsZS4KICAgICAgICAgICAgICAgICAgICBub3JtYWxpemVkTmFtZSA9IG5vcm1hbGl6ZShuYW1lLCBwYXJlbnROYW1lLCBhcHBseU1hcCk7CgogICAgICAgICAgICAgICAgICAgIC8vTm9ybWFsaXplZCBuYW1lIG1heSBiZSBhIHBsdWdpbiBJRCBkdWUgdG8gbWFwIGNvbmZpZwogICAgICAgICAgICAgICAgICAgIC8vYXBwbGljYXRpb24gaW4gbm9ybWFsaXplLiBUaGUgbWFwIGNvbmZpZyB2YWx1ZXMgbXVzdAogICAgICAgICAgICAgICAgICAgIC8vYWxyZWFkeSBiZSBub3JtYWxpemVkLCBzbyBkbyBub3QgbmVlZCB0byByZWRvIHRoYXQgcGFydC4KICAgICAgICAgICAgICAgICAgICBuYW1lUGFydHMgPSBzcGxpdFByZWZpeChub3JtYWxpemVkTmFtZSk7CiAgICAgICAgICAgICAgICAgICAgcHJlZml4ID0gbmFtZVBhcnRzWzBdOwogICAgICAgICAgICAgICAgICAgIG5vcm1hbGl6ZWROYW1lID0gbmFtZVBhcnRzWzFdOwogICAgICAgICAgICAgICAgICAgIGlzTm9ybWFsaXplZCA9IHRydWU7CgogICAgICAgICAgICAgICAgICAgIHVybCA9IGNvbnRleHQubmFtZVRvVXJsKG5vcm1hbGl6ZWROYW1lKTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQoKICAgICAgICAgICAgLy9JZiB0aGUgaWQgaXMgYSBwbHVnaW4gaWQgdGhhdCBjYW5ub3QgYmUgZGV0ZXJtaW5lZCBpZiBpdCBuZWVkcwogICAgICAgICAgICAvL25vcm1hbGl6YXRpb24sIHN0YW1wIGl0IHdpdGggYSB1bmlxdWUgSUQgc28gdHdvIG1hdGNoaW5nIHJlbGF0aXZlCiAgICAgICAgICAgIC8vaWRzIHRoYXQgbWF5IGNvbmZsaWN0IGNhbiBiZSBzZXBhcmF0ZS4KICAgICAgICAgICAgc3VmZml4ID0gcHJlZml4ICYmICFwbHVnaW5Nb2R1bGUgJiYgIWlzTm9ybWFsaXplZCA/CiAgICAgICAgICAgICAgICAgICAgICdfdW5ub3JtYWxpemVkJyArICh1bm5vcm1hbGl6ZWRDb3VudGVyICs9IDEpIDoKICAgICAgICAgICAgICAgICAgICAgJyc7CgogICAgICAgICAgICByZXR1cm4gewogICAgICAgICAgICAgICAgcHJlZml4OiBwcmVmaXgsCiAgICAgICAgICAgICAgICBuYW1lOiBub3JtYWxpemVkTmFtZSwKICAgICAgICAgICAgICAgIHBhcmVudE1hcDogcGFyZW50TW9kdWxlTWFwLAogICAgICAgICAgICAgICAgdW5ub3JtYWxpemVkOiAhIXN1ZmZpeCwKICAgICAgICAgICAgICAgIHVybDogdXJsLAogICAgICAgICAgICAgICAgb3JpZ2luYWxOYW1lOiBvcmlnaW5hbE5hbWUsCiAgICAgICAgICAgICAgICBpc0RlZmluZTogaXNEZWZpbmUsCiAgICAgICAgICAgICAgICBpZDogKHByZWZpeCA/CiAgICAgICAgICAgICAgICAgICAgICAgIHByZWZpeCArICchJyArIG5vcm1hbGl6ZWROYW1lIDoKICAgICAgICAgICAgICAgICAgICAgICAgbm9ybWFsaXplZE5hbWUpICsgc3VmZml4CiAgICAgICAgICAgIH07CiAgICAgICAgfQoKICAgICAgICBmdW5jdGlvbiBnZXRNb2R1bGUoZGVwTWFwKSB7CiAgICAgICAgICAgIHZhciBpZCA9IGRlcE1hcC5pZCwKICAgICAgICAgICAgICAgIG1vZCA9IGdldE93bihyZWdpc3RyeSwgaWQpOwoKICAgICAgICAgICAgaWYgKCFtb2QpIHsKICAgICAgICAgICAgICAgIG1vZCA9IHJlZ2lzdHJ5W2lkXSA9IG5ldyBjb250ZXh0Lk1vZHVsZShkZXBNYXApOwogICAgICAgICAgICB9CgogICAgICAgICAgICByZXR1cm4gbW9kOwogICAgICAgIH0KCiAgICAgICAgZnVuY3Rpb24gb24oZGVwTWFwLCBuYW1lLCBmbikgewogICAgICAgICAgICB2YXIgaWQgPSBkZXBNYXAuaWQsCiAgICAgICAgICAgICAgICBtb2QgPSBnZXRPd24ocmVnaXN0cnksIGlkKTsKCiAgICAgICAgICAgIGlmIChoYXNQcm9wKGRlZmluZWQsIGlkKSAmJgogICAgICAgICAgICAgICAgICAgICghbW9kIHx8IG1vZC5kZWZpbmVFbWl0Q29tcGxldGUpKSB7CiAgICAgICAgICAgICAgICBpZiAobmFtZSA9PT0gJ2RlZmluZWQnKSB7CiAgICAgICAgICAgICAgICAgICAgZm4oZGVmaW5lZFtpZF0pOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICAgICAgbW9kID0gZ2V0TW9kdWxlKGRlcE1hcCk7CiAgICAgICAgICAgICAgICBpZiAobW9kLmVycm9yICYmIG5hbWUgPT09ICdlcnJvcicpIHsKICAgICAgICAgICAgICAgICAgICBmbihtb2QuZXJyb3IpOwogICAgICAgICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgICAgICAgICBtb2Qub24obmFtZSwgZm4pOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CiAgICAgICAgfQoKICAgICAgICBmdW5jdGlvbiBvbkVycm9yKGVyciwgZXJyYmFjaykgewogICAgICAgICAgICB2YXIgaWRzID0gZXJyLnJlcXVpcmVNb2R1bGVzLAogICAgICAgICAgICAgICAgbm90aWZpZWQgPSBmYWxzZTsKCiAgICAgICAgICAgIGlmIChlcnJiYWNrKSB7CiAgICAgICAgICAgICAgICBlcnJiYWNrKGVycik7CiAgICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgICAgICBlYWNoKGlkcywgZnVuY3Rpb24gKGlkKSB7CiAgICAgICAgICAgICAgICAgICAgdmFyIG1vZCA9IGdldE93bihyZWdpc3RyeSwgaWQpOwogICAgICAgICAgICAgICAgICAgIGlmIChtb2QpIHsKICAgICAgICAgICAgICAgICAgICAgICAgLy9TZXQgZXJyb3Igb24gbW9kdWxlLCBzbyBpdCBza2lwcyB0aW1lb3V0IGNoZWNrcy4KICAgICAgICAgICAgICAgICAgICAgICAgbW9kLmVycm9yID0gZXJyOwogICAgICAgICAgICAgICAgICAgICAgICBpZiAobW9kLmV2ZW50cy5lcnJvcikgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgbm90aWZpZWQgPSB0cnVlOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9kLmVtaXQoJ2Vycm9yJywgZXJyKTsKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIH0pOwoKICAgICAgICAgICAgICAgIGlmICghbm90aWZpZWQpIHsKICAgICAgICAgICAgICAgICAgICByZXEub25FcnJvcihlcnIpOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CiAgICAgICAgfQoKICAgICAgICAvKioKICAgICAgICAgKiBJbnRlcm5hbCBtZXRob2QgdG8gdHJhbnNmZXIgZ2xvYmFsUXVldWUgaXRlbXMgdG8gdGhpcyBjb250ZXh0J3MKICAgICAgICAgKiBkZWZRdWV1ZS4KICAgICAgICAgKi8KICAgICAgICBmdW5jdGlvbiB0YWtlR2xvYmFsUXVldWUoKSB7CiAgICAgICAgICAgIC8vUHVzaCBhbGwgdGhlIGdsb2JhbERlZlF1ZXVlIGl0ZW1zIGludG8gdGhlIGNvbnRleHQncyBkZWZRdWV1ZQogICAgICAgICAgICBpZiAoZ2xvYmFsRGVmUXVldWUubGVuZ3RoKSB7CiAgICAgICAgICAgICAgICBlYWNoKGdsb2JhbERlZlF1ZXVlLCBmdW5jdGlvbihxdWV1ZUl0ZW0pIHsKICAgICAgICAgICAgICAgICAgICB2YXIgaWQgPSBxdWV1ZUl0ZW1bMF07CiAgICAgICAgICAgICAgICAgICAgaWYgKHR5cGVvZiBpZCA9PT0gJ3N0cmluZycpIHsKICAgICAgICAgICAgICAgICAgICAgICAgY29udGV4dC5kZWZRdWV1ZU1hcFtpZF0gPSB0cnVlOwogICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICBkZWZRdWV1ZS5wdXNoKHF1ZXVlSXRlbSk7CiAgICAgICAgICAgICAgICB9KTsKICAgICAgICAgICAgICAgIGdsb2JhbERlZlF1ZXVlID0gW107CiAgICAgICAgICAgIH0KICAgICAgICB9CgogICAgICAgIGhhbmRsZXJzID0gewogICAgICAgICAgICAncmVxdWlyZSc6IGZ1bmN0aW9uIChtb2QpIHsKICAgICAgICAgICAgICAgIGlmIChtb2QucmVxdWlyZSkgewogICAgICAgICAgICAgICAgICAgIHJldHVybiBtb2QucmVxdWlyZTsKICAgICAgICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIChtb2QucmVxdWlyZSA9IGNvbnRleHQubWFrZVJlcXVpcmUobW9kLm1hcCkpOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAnZXhwb3J0cyc6IGZ1bmN0aW9uIChtb2QpIHsKICAgICAgICAgICAgICAgIG1vZC51c2luZ0V4cG9ydHMgPSB0cnVlOwogICAgICAgICAgICAgICAgaWYgKG1vZC5tYXAuaXNEZWZpbmUpIHsKICAgICAgICAgICAgICAgICAgICBpZiAobW9kLmV4cG9ydHMpIHsKICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIChkZWZpbmVkW21vZC5tYXAuaWRdID0gbW9kLmV4cG9ydHMpOwogICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiAobW9kLmV4cG9ydHMgPSBkZWZpbmVkW21vZC5tYXAuaWRdID0ge30pOwogICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgJ21vZHVsZSc6IGZ1bmN0aW9uIChtb2QpIHsKICAgICAgICAgICAgICAgIGlmIChtb2QubW9kdWxlKSB7CiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIG1vZC5tb2R1bGU7CiAgICAgICAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICAgICAgICAgIHJldHVybiAobW9kLm1vZHVsZSA9IHsKICAgICAgICAgICAgICAgICAgICAgICAgaWQ6IG1vZC5tYXAuaWQsCiAgICAgICAgICAgICAgICAgICAgICAgIHVyaTogbW9kLm1hcC51cmwsCiAgICAgICAgICAgICAgICAgICAgICAgIGNvbmZpZzogZnVuY3Rpb24gKCkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGdldE93bihjb25maWcuY29uZmlnLCBtb2QubWFwLmlkKSB8fCB7fTsKICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgZXhwb3J0czogbW9kLmV4cG9ydHMgfHwgKG1vZC5leHBvcnRzID0ge30pCiAgICAgICAgICAgICAgICAgICAgfSk7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICB9OwoKICAgICAgICBmdW5jdGlvbiBjbGVhblJlZ2lzdHJ5KGlkKSB7CiAgICAgICAgICAgIC8vQ2xlYW4gdXAgbWFjaGluZXJ5IHVzZWQgZm9yIHdhaXRpbmcgbW9kdWxlcy4KICAgICAgICAgICAgZGVsZXRlIHJlZ2lzdHJ5W2lkXTsKICAgICAgICAgICAgZGVsZXRlIGVuYWJsZWRSZWdpc3RyeVtpZF07CiAgICAgICAgfQoKICAgICAgICBmdW5jdGlvbiBicmVha0N5Y2xlKG1vZCwgdHJhY2VkLCBwcm9jZXNzZWQpIHsKICAgICAgICAgICAgdmFyIGlkID0gbW9kLm1hcC5pZDsKCiAgICAgICAgICAgIGlmIChtb2QuZXJyb3IpIHsKICAgICAgICAgICAgICAgIG1vZC5lbWl0KCdlcnJvcicsIG1vZC5lcnJvcik7CiAgICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgICAgICB0cmFjZWRbaWRdID0gdHJ1ZTsKICAgICAgICAgICAgICAgIGVhY2gobW9kLmRlcE1hcHMsIGZ1bmN0aW9uIChkZXBNYXAsIGkpIHsKICAgICAgICAgICAgICAgICAgICB2YXIgZGVwSWQgPSBkZXBNYXAuaWQsCiAgICAgICAgICAgICAgICAgICAgICAgIGRlcCA9IGdldE93bihyZWdpc3RyeSwgZGVwSWQpOwoKICAgICAgICAgICAgICAgICAgICAvL09ubHkgZm9yY2UgdGhpbmdzIHRoYXQgaGF2ZSBub3QgY29tcGxldGVkCiAgICAgICAgICAgICAgICAgICAgLy9iZWluZyBkZWZpbmVkLCBzbyBzdGlsbCBpbiB0aGUgcmVnaXN0cnksCiAgICAgICAgICAgICAgICAgICAgLy9hbmQgb25seSBpZiBpdCBoYXMgbm90IGJlZW4gbWF0Y2hlZCB1cAogICAgICAgICAgICAgICAgICAgIC8vaW4gdGhlIG1vZHVsZSBhbHJlYWR5LgogICAgICAgICAgICAgICAgICAgIGlmIChkZXAgJiYgIW1vZC5kZXBNYXRjaGVkW2ldICYmICFwcm9jZXNzZWRbZGVwSWRdKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChnZXRPd24odHJhY2VkLCBkZXBJZCkpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1vZC5kZWZpbmVEZXAoaSwgZGVmaW5lZFtkZXBJZF0pOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9kLmNoZWNrKCk7IC8vcGFzcyBmYWxzZT8KICAgICAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrQ3ljbGUoZGVwLCB0cmFjZWQsIHByb2Nlc3NlZCk7CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICB9KTsKICAgICAgICAgICAgICAgIHByb2Nlc3NlZFtpZF0gPSB0cnVlOwogICAgICAgICAgICB9CiAgICAgICAgfQoKICAgICAgICBmdW5jdGlvbiBjaGVja0xvYWRlZCgpIHsKICAgICAgICAgICAgdmFyIGVyciwgdXNpbmdQYXRoRmFsbGJhY2ssCiAgICAgICAgICAgICAgICB3YWl0SW50ZXJ2YWwgPSBjb25maWcud2FpdFNlY29uZHMgKiAxMDAwLAogICAgICAgICAgICAgICAgLy9JdCBpcyBwb3NzaWJsZSB0byBkaXNhYmxlIHRoZSB3YWl0IGludGVydmFsIGJ5IHVzaW5nIHdhaXRTZWNvbmRzIG9mIDAuCiAgICAgICAgICAgICAgICBleHBpcmVkID0gd2FpdEludGVydmFsICYmIChjb250ZXh0LnN0YXJ0VGltZSArIHdhaXRJbnRlcnZhbCkgPCBuZXcgRGF0ZSgpLmdldFRpbWUoKSwKICAgICAgICAgICAgICAgIG5vTG9hZHMgPSBbXSwKICAgICAgICAgICAgICAgIHJlcUNhbGxzID0gW10sCiAgICAgICAgICAgICAgICBzdGlsbExvYWRpbmcgPSBmYWxzZSwKICAgICAgICAgICAgICAgIG5lZWRDeWNsZUNoZWNrID0gdHJ1ZTsKCiAgICAgICAgICAgIC8vRG8gbm90IGJvdGhlciBpZiB0aGlzIGNhbGwgd2FzIGEgcmVzdWx0IG9mIGEgY3ljbGUgYnJlYWsuCiAgICAgICAgICAgIGlmIChpbkNoZWNrTG9hZGVkKSB7CiAgICAgICAgICAgICAgICByZXR1cm47CiAgICAgICAgICAgIH0KCiAgICAgICAgICAgIGluQ2hlY2tMb2FkZWQgPSB0cnVlOwoKICAgICAgICAgICAgLy9GaWd1cmUgb3V0IHRoZSBzdGF0ZSBvZiBhbGwgdGhlIG1vZHVsZXMuCiAgICAgICAgICAgIGVhY2hQcm9wKGVuYWJsZWRSZWdpc3RyeSwgZnVuY3Rpb24gKG1vZCkgewogICAgICAgICAgICAgICAgdmFyIG1hcCA9IG1vZC5tYXAsCiAgICAgICAgICAgICAgICAgICAgbW9kSWQgPSBtYXAuaWQ7CgogICAgICAgICAgICAgICAgLy9Ta2lwIHRoaW5ncyB0aGF0IGFyZSBub3QgZW5hYmxlZCBvciBpbiBlcnJvciBzdGF0ZS4KICAgICAgICAgICAgICAgIGlmICghbW9kLmVuYWJsZWQpIHsKICAgICAgICAgICAgICAgICAgICByZXR1cm47CiAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgaWYgKCFtYXAuaXNEZWZpbmUpIHsKICAgICAgICAgICAgICAgICAgICByZXFDYWxscy5wdXNoKG1vZCk7CiAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgaWYgKCFtb2QuZXJyb3IpIHsKICAgICAgICAgICAgICAgICAgICAvL0lmIHRoZSBtb2R1bGUgc2hvdWxkIGJlIGV4ZWN1dGVkLCBhbmQgaXQgaGFzIG5vdAogICAgICAgICAgICAgICAgICAgIC8vYmVlbiBpbml0ZWQgYW5kIHRpbWUgaXMgdXAsIHJlbWVtYmVyIGl0LgogICAgICAgICAgICAgICAgICAgIGlmICghbW9kLmluaXRlZCAmJiBleHBpcmVkKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChoYXNQYXRoRmFsbGJhY2sobW9kSWQpKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB1c2luZ1BhdGhGYWxsYmFjayA9IHRydWU7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdGlsbExvYWRpbmcgPSB0cnVlOwogICAgICAgICAgICAgICAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgbm9Mb2Fkcy5wdXNoKG1vZElkKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlbW92ZVNjcmlwdChtb2RJZCk7CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKCFtb2QuaW5pdGVkICYmIG1vZC5mZXRjaGVkICYmIG1hcC5pc0RlZmluZSkgewogICAgICAgICAgICAgICAgICAgICAgICBzdGlsbExvYWRpbmcgPSB0cnVlOwogICAgICAgICAgICAgICAgICAgICAgICBpZiAoIW1hcC5wcmVmaXgpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vTm8gcmVhc29uIHRvIGtlZXAgbG9va2luZyBmb3IgdW5maW5pc2hlZAogICAgICAgICAgICAgICAgICAgICAgICAgICAgLy9sb2FkaW5nLiBJZiB0aGUgb25seSBzdGlsbExvYWRpbmcgaXMgYQogICAgICAgICAgICAgICAgICAgICAgICAgICAgLy9wbHVnaW4gcmVzb3VyY2UgdGhvdWdoLCBrZWVwIGdvaW5nLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgLy9iZWNhdXNlIGl0IG1heSBiZSB0aGF0IGEgcGx1Z2luIHJlc291cmNlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvL2lzIHdhaXRpbmcgb24gYSBub24tcGx1Z2luIGN5Y2xlLgogICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIChuZWVkQ3ljbGVDaGVjayA9IGZhbHNlKTsKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSk7CgogICAgICAgICAgICBpZiAoZXhwaXJlZCAmJiBub0xvYWRzLmxlbmd0aCkgewogICAgICAgICAgICAgICAgLy9JZiB3YWl0IHRpbWUgZXhwaXJlZCwgdGhyb3cgZXJyb3Igb2YgdW5sb2FkZWQgbW9kdWxlcy4KICAgICAgICAgICAgICAgIGVyciA9IG1ha2VFcnJvcigndGltZW91dCcsICdMb2FkIHRpbWVvdXQgZm9yIG1vZHVsZXM6ICcgKyBub0xvYWRzLCBudWxsLCBub0xvYWRzKTsKICAgICAgICAgICAgICAgIGVyci5jb250ZXh0TmFtZSA9IGNvbnRleHQuY29udGV4dE5hbWU7CiAgICAgICAgICAgICAgICByZXR1cm4gb25FcnJvcihlcnIpOwogICAgICAgICAgICB9CgogICAgICAgICAgICAvL05vdCBleHBpcmVkLCBjaGVjayBmb3IgYSBjeWNsZS4KICAgICAgICAgICAgaWYgKG5lZWRDeWNsZUNoZWNrKSB7CiAgICAgICAgICAgICAgICBlYWNoKHJlcUNhbGxzLCBmdW5jdGlvbiAobW9kKSB7CiAgICAgICAgICAgICAgICAgICAgYnJlYWtDeWNsZShtb2QsIHt9LCB7fSk7CiAgICAgICAgICAgICAgICB9KTsKICAgICAgICAgICAgfQoKICAgICAgICAgICAgLy9JZiBzdGlsbCB3YWl0aW5nIG9uIGxvYWRzLCBhbmQgdGhlIHdhaXRpbmcgbG9hZCBpcyBzb21ldGhpbmcKICAgICAgICAgICAgLy9vdGhlciB0aGFuIGEgcGx1Z2luIHJlc291cmNlLCBvciB0aGVyZSBhcmUgc3RpbGwgb3V0c3RhbmRpbmcKICAgICAgICAgICAgLy9zY3JpcHRzLCB0aGVuIGp1c3QgdHJ5IGJhY2sgbGF0ZXIuCiAgICAgICAgICAgIGlmICgoIWV4cGlyZWQgfHwgdXNpbmdQYXRoRmFsbGJhY2spICYmIHN0aWxsTG9hZGluZykgewogICAgICAgICAgICAgICAgLy9Tb21ldGhpbmcgaXMgc3RpbGwgd2FpdGluZyB0byBsb2FkLiBXYWl0IGZvciBpdCwgYnV0IG9ubHkKICAgICAgICAgICAgICAgIC8vaWYgYSB0aW1lb3V0IGlzIG5vdCBhbHJlYWR5IGluIGVmZmVjdC4KICAgICAgICAgICAgICAgIGlmICgoaXNCcm93c2VyIHx8IGlzV2ViV29ya2VyKSAmJiAhY2hlY2tMb2FkZWRUaW1lb3V0SWQpIHsKICAgICAgICAgICAgICAgICAgICBjaGVja0xvYWRlZFRpbWVvdXRJZCA9IHNldFRpbWVvdXQoZnVuY3Rpb24gKCkgewogICAgICAgICAgICAgICAgICAgICAgICBjaGVja0xvYWRlZFRpbWVvdXRJZCA9IDA7CiAgICAgICAgICAgICAgICAgICAgICAgIGNoZWNrTG9hZGVkKCk7CiAgICAgICAgICAgICAgICAgICAgfSwgNTApOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CgogICAgICAgICAgICBpbkNoZWNrTG9hZGVkID0gZmFsc2U7CiAgICAgICAgfQoKICAgICAgICBNb2R1bGUgPSBmdW5jdGlvbiAobWFwKSB7CiAgICAgICAgICAgIHRoaXMuZXZlbnRzID0gZ2V0T3duKHVuZGVmRXZlbnRzLCBtYXAuaWQpIHx8IHt9OwogICAgICAgICAgICB0aGlzLm1hcCA9IG1hcDsKICAgICAgICAgICAgdGhpcy5zaGltID0gZ2V0T3duKGNvbmZpZy5zaGltLCBtYXAuaWQpOwogICAgICAgICAgICB0aGlzLmRlcEV4cG9ydHMgPSBbXTsKICAgICAgICAgICAgdGhpcy5kZXBNYXBzID0gW107CiAgICAgICAgICAgIHRoaXMuZGVwTWF0Y2hlZCA9IFtdOwogICAgICAgICAgICB0aGlzLnBsdWdpbk1hcHMgPSB7fTsKICAgICAgICAgICAgdGhpcy5kZXBDb3VudCA9IDA7CgogICAgICAgICAgICAvKiB0aGlzLmV4cG9ydHMgdGhpcy5mYWN0b3J5CiAgICAgICAgICAgICAgIHRoaXMuZGVwTWFwcyA9IFtdLAogICAgICAgICAgICAgICB0aGlzLmVuYWJsZWQsIHRoaXMuZmV0Y2hlZAogICAgICAgICAgICAqLwogICAgICAgIH07CgogICAgICAgIE1vZHVsZS5wcm90b3R5cGUgPSB7CiAgICAgICAgICAgIGluaXQ6IGZ1bmN0aW9uIChkZXBNYXBzLCBmYWN0b3J5LCBlcnJiYWNrLCBvcHRpb25zKSB7CiAgICAgICAgICAgICAgICBvcHRpb25zID0gb3B0aW9ucyB8fCB7fTsKCiAgICAgICAgICAgICAgICAvL0RvIG5vdCBkbyBtb3JlIGluaXRzIGlmIGFscmVhZHkgZG9uZS4gQ2FuIGhhcHBlbiBpZiB0aGVyZQogICAgICAgICAgICAgICAgLy9hcmUgbXVsdGlwbGUgZGVmaW5lIGNhbGxzIGZvciB0aGUgc2FtZSBtb2R1bGUuIFRoYXQgaXMgbm90CiAgICAgICAgICAgICAgICAvL2Egbm9ybWFsLCBjb21tb24gY2FzZSwgYnV0IGl0IGlzIGFsc28gbm90IHVuZXhwZWN0ZWQuCiAgICAgICAgICAgICAgICBpZiAodGhpcy5pbml0ZWQpIHsKICAgICAgICAgICAgICAgICAgICByZXR1cm47CiAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgdGhpcy5mYWN0b3J5ID0gZmFjdG9yeTsKCiAgICAgICAgICAgICAgICBpZiAoZXJyYmFjaykgewogICAgICAgICAgICAgICAgICAgIC8vUmVnaXN0ZXIgZm9yIGVycm9ycyBvbiB0aGlzIG1vZHVsZS4KICAgICAgICAgICAgICAgICAgICB0aGlzLm9uKCdlcnJvcicsIGVycmJhY2spOwogICAgICAgICAgICAgICAgfSBlbHNlIGlmICh0aGlzLmV2ZW50cy5lcnJvcikgewogICAgICAgICAgICAgICAgICAgIC8vSWYgbm8gZXJyYmFjayBhbHJlYWR5LCBidXQgdGhlcmUgYXJlIGVycm9yIGxpc3RlbmVycwogICAgICAgICAgICAgICAgICAgIC8vb24gdGhpcyBtb2R1bGUsIHNldCB1cCBhbiBlcnJiYWNrIHRvIHBhc3MgdG8gdGhlIGRlcHMuCiAgICAgICAgICAgICAgICAgICAgZXJyYmFjayA9IGJpbmQodGhpcywgZnVuY3Rpb24gKGVycikgewogICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmVtaXQoJ2Vycm9yJywgZXJyKTsKICAgICAgICAgICAgICAgICAgICB9KTsKICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICAvL0RvIGEgY29weSBvZiB0aGUgZGVwZW5kZW5jeSBhcnJheSwgc28gdGhhdAogICAgICAgICAgICAgICAgLy9zb3VyY2UgaW5wdXRzIGFyZSBub3QgbW9kaWZpZWQuIEZvciBleGFtcGxlCiAgICAgICAgICAgICAgICAvLyJzaGltIiBkZXBzIGFyZSBwYXNzZWQgaW4gaGVyZSBkaXJlY3RseSwgYW5kCiAgICAgICAgICAgICAgICAvL2RvaW5nIGEgZGlyZWN0IG1vZGlmaWNhdGlvbiBvZiB0aGUgZGVwTWFwcyBhcnJheQogICAgICAgICAgICAgICAgLy93b3VsZCBhZmZlY3QgdGhhdCBjb25maWcuCiAgICAgICAgICAgICAgICB0aGlzLmRlcE1hcHMgPSBkZXBNYXBzICYmIGRlcE1hcHMuc2xpY2UoMCk7CgogICAgICAgICAgICAgICAgdGhpcy5lcnJiYWNrID0gZXJyYmFjazsKCiAgICAgICAgICAgICAgICAvL0luZGljYXRlIHRoaXMgbW9kdWxlIGhhcyBiZSBpbml0aWFsaXplZAogICAgICAgICAgICAgICAgdGhpcy5pbml0ZWQgPSB0cnVlOwoKICAgICAgICAgICAgICAgIHRoaXMuaWdub3JlID0gb3B0aW9ucy5pZ25vcmU7CgogICAgICAgICAgICAgICAgLy9Db3VsZCBoYXZlIG9wdGlvbiB0byBpbml0IHRoaXMgbW9kdWxlIGluIGVuYWJsZWQgbW9kZSwKICAgICAgICAgICAgICAgIC8vb3IgY291bGQgaGF2ZSBiZWVuIHByZXZpb3VzbHkgbWFya2VkIGFzIGVuYWJsZWQuIEhvd2V2ZXIsCiAgICAgICAgICAgICAgICAvL3RoZSBkZXBlbmRlbmNpZXMgYXJlIG5vdCBrbm93biB1bnRpbCBpbml0IGlzIGNhbGxlZC4gU28KICAgICAgICAgICAgICAgIC8vaWYgZW5hYmxlZCBwcmV2aW91c2x5LCBub3cgdHJpZ2dlciBkZXBlbmRlbmNpZXMgYXMgZW5hYmxlZC4KICAgICAgICAgICAgICAgIGlmIChvcHRpb25zLmVuYWJsZWQgfHwgdGhpcy5lbmFibGVkKSB7CiAgICAgICAgICAgICAgICAgICAgLy9FbmFibGUgdGhpcyBtb2R1bGUgYW5kIGRlcGVuZGVuY2llcy4KICAgICAgICAgICAgICAgICAgICAvL1dpbGwgY2FsbCB0aGlzLmNoZWNrKCkKICAgICAgICAgICAgICAgICAgICB0aGlzLmVuYWJsZSgpOwogICAgICAgICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgICAgICAgICB0aGlzLmNoZWNrKCk7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCgogICAgICAgICAgICBkZWZpbmVEZXA6IGZ1bmN0aW9uIChpLCBkZXBFeHBvcnRzKSB7CiAgICAgICAgICAgICAgICAvL0JlY2F1c2Ugb2YgY3ljbGVzLCBkZWZpbmVkIGNhbGxiYWNrIGZvciBhIGdpdmVuCiAgICAgICAgICAgICAgICAvL2V4cG9ydCBjYW4gYmUgY2FsbGVkIG1vcmUgdGhhbiBvbmNlLgogICAgICAgICAgICAgICAgaWYgKCF0aGlzLmRlcE1hdGNoZWRbaV0pIHsKICAgICAgICAgICAgICAgICAgICB0aGlzLmRlcE1hdGNoZWRbaV0gPSB0cnVlOwogICAgICAgICAgICAgICAgICAgIHRoaXMuZGVwQ291bnQgLT0gMTsKICAgICAgICAgICAgICAgICAgICB0aGlzLmRlcEV4cG9ydHNbaV0gPSBkZXBFeHBvcnRzOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAoKICAgICAgICAgICAgZmV0Y2g6IGZ1bmN0aW9uICgpIHsKICAgICAgICAgICAgICAgIGlmICh0aGlzLmZldGNoZWQpIHsKICAgICAgICAgICAgICAgICAgICByZXR1cm47CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICB0aGlzLmZldGNoZWQgPSB0cnVlOwoKICAgICAgICAgICAgICAgIGNvbnRleHQuc3RhcnRUaW1lID0gKG5ldyBEYXRlKCkpLmdldFRpbWUoKTsKCiAgICAgICAgICAgICAgICB2YXIgbWFwID0gdGhpcy5tYXA7CgogICAgICAgICAgICAgICAgLy9JZiB0aGUgbWFuYWdlciBpcyBmb3IgYSBwbHVnaW4gbWFuYWdlZCByZXNvdXJjZSwKICAgICAgICAgICAgICAgIC8vYXNrIHRoZSBwbHVnaW4gdG8gbG9hZCBpdCBub3cuCiAgICAgICAgICAgICAgICBpZiAodGhpcy5zaGltKSB7CiAgICAgICAgICAgICAgICAgICAgY29udGV4dC5tYWtlUmVxdWlyZSh0aGlzLm1hcCwgewogICAgICAgICAgICAgICAgICAgICAgICBlbmFibGVCdWlsZENhbGxiYWNrOiB0cnVlCiAgICAgICAgICAgICAgICAgICAgfSkodGhpcy5zaGltLmRlcHMgfHwgW10sIGJpbmQodGhpcywgZnVuY3Rpb24gKCkgewogICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gbWFwLnByZWZpeCA/IHRoaXMuY2FsbFBsdWdpbigpIDogdGhpcy5sb2FkKCk7CiAgICAgICAgICAgICAgICAgICAgfSkpOwogICAgICAgICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgICAgICAgICAvL1JlZ3VsYXIgZGVwZW5kZW5jeS4KICAgICAgICAgICAgICAgICAgICByZXR1cm4gbWFwLnByZWZpeCA/IHRoaXMuY2FsbFBsdWdpbigpIDogdGhpcy5sb2FkKCk7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCgogICAgICAgICAgICBsb2FkOiBmdW5jdGlvbiAoKSB7CiAgICAgICAgICAgICAgICB2YXIgdXJsID0gdGhpcy5tYXAudXJsOwoKICAgICAgICAgICAgICAgIC8vUmVndWxhciBkZXBlbmRlbmN5LgogICAgICAgICAgICAgICAgaWYgKCF1cmxGZXRjaGVkW3VybF0pIHsKICAgICAgICAgICAgICAgICAgICB1cmxGZXRjaGVkW3VybF0gPSB0cnVlOwogICAgICAgICAgICAgICAgICAgIGNvbnRleHQubG9hZCh0aGlzLm1hcC5pZCwgdXJsKTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKCiAgICAgICAgICAgIC8qKgogICAgICAgICAgICAgKiBDaGVja3MgaWYgdGhlIG1vZHVsZSBpcyByZWFkeSB0byBkZWZpbmUgaXRzZWxmLCBhbmQgaWYgc28sCiAgICAgICAgICAgICAqIGRlZmluZSBpdC4KICAgICAgICAgICAgICovCiAgICAgICAgICAgIGNoZWNrOiBmdW5jdGlvbiAoKSB7CiAgICAgICAgICAgICAgICBpZiAoIXRoaXMuZW5hYmxlZCB8fCB0aGlzLmVuYWJsaW5nKSB7CiAgICAgICAgICAgICAgICAgICAgcmV0dXJuOwogICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgIHZhciBlcnIsIGNqc01vZHVsZSwKICAgICAgICAgICAgICAgICAgICBpZCA9IHRoaXMubWFwLmlkLAogICAgICAgICAgICAgICAgICAgIGRlcEV4cG9ydHMgPSB0aGlzLmRlcEV4cG9ydHMsCiAgICAgICAgICAgICAgICAgICAgZXhwb3J0cyA9IHRoaXMuZXhwb3J0cywKICAgICAgICAgICAgICAgICAgICBmYWN0b3J5ID0gdGhpcy5mYWN0b3J5OwoKICAgICAgICAgICAgICAgIGlmICghdGhpcy5pbml0ZWQpIHsKICAgICAgICAgICAgICAgICAgICAvLyBPbmx5IGZldGNoIGlmIG5vdCBhbHJlYWR5IGluIHRoZSBkZWZRdWV1ZS4KICAgICAgICAgICAgICAgICAgICBpZiAoIWhhc1Byb3AoY29udGV4dC5kZWZRdWV1ZU1hcCwgaWQpKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuZmV0Y2goKTsKICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKHRoaXMuZXJyb3IpIHsKICAgICAgICAgICAgICAgICAgICB0aGlzLmVtaXQoJ2Vycm9yJywgdGhpcy5lcnJvcik7CiAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKCF0aGlzLmRlZmluaW5nKSB7CiAgICAgICAgICAgICAgICAgICAgLy9UaGUgZmFjdG9yeSBjb3VsZCB0cmlnZ2VyIGFub3RoZXIgcmVxdWlyZSBjYWxsCiAgICAgICAgICAgICAgICAgICAgLy90aGF0IHdvdWxkIHJlc3VsdCBpbiBjaGVja2luZyB0aGlzIG1vZHVsZSB0bwogICAgICAgICAgICAgICAgICAgIC8vZGVmaW5lIGl0c2VsZiBhZ2Fpbi4gSWYgYWxyZWFkeSBpbiB0aGUgcHJvY2VzcwogICAgICAgICAgICAgICAgICAgIC8vb2YgZG9pbmcgdGhhdCwgc2tpcCB0aGlzIHdvcmsuCiAgICAgICAgICAgICAgICAgICAgdGhpcy5kZWZpbmluZyA9IHRydWU7CgogICAgICAgICAgICAgICAgICAgIGlmICh0aGlzLmRlcENvdW50IDwgMSAmJiAhdGhpcy5kZWZpbmVkKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChpc0Z1bmN0aW9uKGZhY3RvcnkpKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0cnkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4cG9ydHMgPSBjb250ZXh0LmV4ZWNDYihpZCwgZmFjdG9yeSwgZGVwRXhwb3J0cywgZXhwb3J0cyk7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IGNhdGNoIChlKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZXJyID0gZTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBGYXZvciByZXR1cm4gdmFsdWUgb3ZlciBleHBvcnRzLiBJZiBub2RlL2NqcyBpbiBwbGF5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gdGhlbiB3aWxsIG5vdCBoYXZlIGEgcmV0dXJuIHZhbHVlIGFueXdheS4gRmF2b3IKICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIG1vZHVsZS5leHBvcnRzIGFzc2lnbm1lbnQgb3ZlciBleHBvcnRzIG9iamVjdC4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmICh0aGlzLm1hcC5pc0RlZmluZSAmJiBleHBvcnRzID09PSB1bmRlZmluZWQpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjanNNb2R1bGUgPSB0aGlzLm1vZHVsZTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAoY2pzTW9kdWxlKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4cG9ydHMgPSBjanNNb2R1bGUuZXhwb3J0czsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKHRoaXMudXNpbmdFeHBvcnRzKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vZXhwb3J0cyBhbHJlYWR5IHNldCB0aGUgZGVmaW5lZCB2YWx1ZS4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZXhwb3J0cyA9IHRoaXMuZXhwb3J0czsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGVycikgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIElmIHRoZXJlIGlzIGFuIGVycm9yIGxpc3RlbmVyLCBmYXZvciBwYXNzaW5nCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gdG8gdGhhdCBpbnN0ZWFkIG9mIHRocm93aW5nIGFuIGVycm9yLiBIb3dldmVyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIG9ubHkgZG8gaXQgZm9yIGRlZmluZSgpJ2QgIG1vZHVsZXMuIHJlcXVpcmUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBlcnJiYWNrcyBzaG91bGQgbm90IGJlIGNhbGxlZCBmb3IgZmFpbHVyZXMgaW4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyB0aGVpciBjYWxsYmFja3MgKCM2OTkpLiBIb3dldmVyIGlmIGEgZ2xvYmFsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gb25FcnJvciBpcyBzZXQsIHVzZSB0aGF0LgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmICgodGhpcy5ldmVudHMuZXJyb3IgJiYgdGhpcy5tYXAuaXNEZWZpbmUpIHx8CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcS5vbkVycm9yICE9PSBkZWZhdWx0T25FcnJvcikgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlcnIucmVxdWlyZU1hcCA9IHRoaXMubWFwOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlcnIucmVxdWlyZU1vZHVsZXMgPSB0aGlzLm1hcC5pc0RlZmluZSA/IFt0aGlzLm1hcC5pZF0gOiBudWxsOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlcnIucmVxdWlyZVR5cGUgPSB0aGlzLm1hcC5pc0RlZmluZSA/ICdkZWZpbmUnIDogJ3JlcXVpcmUnOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gb25FcnJvcigodGhpcy5lcnJvciA9IGVycikpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAodHlwZW9mIGNvbnNvbGUgIT09ICd1bmRlZmluZWQnICYmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIExvZyB0aGUgZXJyb3IgZm9yIGRlYnVnZ2luZy4gSWYgcHJvbWlzZXMgY291bGQgYmUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gdXNlZCwgdGhpcyB3b3VsZCBiZSBkaWZmZXJlbnQsIGJ1dCBtYWtpbmcgZG8uCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoZXJyKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBEbyBub3Qgd2FudCB0byBjb21wbGV0ZWx5IGxvc2UgdGhlIGVycm9yLiBXaGlsZSB0aGlzCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIHdpbGwgbWVzcyB1cCBwcm9jZXNzaW5nIGFuZCBsZWFkIHRvIHNpbWlsYXIgcmVzdWx0cwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBhcyBidWcgMTQ0MCwgaXQgYXQgbGVhc3Qgc3VyZmFjZXMgdGhlIGVycm9yLgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXEub25FcnJvcihlcnIpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vSnVzdCBhIGxpdGVyYWwgdmFsdWUKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4cG9ydHMgPSBmYWN0b3J5OwogICAgICAgICAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmV4cG9ydHMgPSBleHBvcnRzOwoKICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHRoaXMubWFwLmlzRGVmaW5lICYmICF0aGlzLmlnbm9yZSkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVmaW5lZFtpZF0gPSBleHBvcnRzOwoKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIChyZXEub25SZXNvdXJjZUxvYWQpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXIgcmVzTG9hZE1hcHMgPSBbXTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlYWNoKHRoaXMuZGVwTWFwcywgZnVuY3Rpb24gKGRlcE1hcCkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXNMb2FkTWFwcy5wdXNoKGRlcE1hcC5ub3JtYWxpemVkTWFwIHx8IGRlcE1hcCk7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSk7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVxLm9uUmVzb3VyY2VMb2FkKGNvbnRleHQsIHRoaXMubWFwLCByZXNMb2FkTWFwcyk7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICAgICAgICAgIC8vQ2xlYW4gdXAKICAgICAgICAgICAgICAgICAgICAgICAgY2xlYW5SZWdpc3RyeShpZCk7CgogICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmRlZmluZWQgPSB0cnVlOwogICAgICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICAgICAgLy9GaW5pc2hlZCB0aGUgZGVmaW5lIHN0YWdlLiBBbGxvdyBjYWxsaW5nIGNoZWNrIGFnYWluCiAgICAgICAgICAgICAgICAgICAgLy90byBhbGxvdyBkZWZpbmUgbm90aWZpY2F0aW9ucyBiZWxvdyBpbiB0aGUgY2FzZSBvZiBhCiAgICAgICAgICAgICAgICAgICAgLy9jeWNsZS4KICAgICAgICAgICAgICAgICAgICB0aGlzLmRlZmluaW5nID0gZmFsc2U7CgogICAgICAgICAgICAgICAgICAgIGlmICh0aGlzLmRlZmluZWQgJiYgIXRoaXMuZGVmaW5lRW1pdHRlZCkgewogICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmRlZmluZUVtaXR0ZWQgPSB0cnVlOwogICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmVtaXQoJ2RlZmluZWQnLCB0aGlzLmV4cG9ydHMpOwogICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmRlZmluZUVtaXRDb21wbGV0ZSA9IHRydWU7CiAgICAgICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKCiAgICAgICAgICAgIGNhbGxQbHVnaW46IGZ1bmN0aW9uICgpIHsKICAgICAgICAgICAgICAgIHZhciBtYXAgPSB0aGlzLm1hcCwKICAgICAgICAgICAgICAgICAgICBpZCA9IG1hcC5pZCwKICAgICAgICAgICAgICAgICAgICAvL01hcCBhbHJlYWR5IG5vcm1hbGl6ZWQgdGhlIHByZWZpeC4KICAgICAgICAgICAgICAgICAgICBwbHVnaW5NYXAgPSBtYWtlTW9kdWxlTWFwKG1hcC5wcmVmaXgpOwoKICAgICAgICAgICAgICAgIC8vTWFyayB0aGlzIGFzIGEgZGVwZW5kZW5jeSBmb3IgdGhpcyBwbHVnaW4sIHNvIGl0CiAgICAgICAgICAgICAgICAvL2NhbiBiZSB0cmFjZWQgZm9yIGN5Y2xlcy4KICAgICAgICAgICAgICAgIHRoaXMuZGVwTWFwcy5wdXNoKHBsdWdpbk1hcCk7CgogICAgICAgICAgICAgICAgb24ocGx1Z2luTWFwLCAnZGVmaW5lZCcsIGJpbmQodGhpcywgZnVuY3Rpb24gKHBsdWdpbikgewogICAgICAgICAgICAgICAgICAgIHZhciBsb2FkLCBub3JtYWxpemVkTWFwLCBub3JtYWxpemVkTW9kLAogICAgICAgICAgICAgICAgICAgICAgICBidW5kbGVJZCA9IGdldE93bihidW5kbGVzTWFwLCB0aGlzLm1hcC5pZCksCiAgICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSB0aGlzLm1hcC5uYW1lLAogICAgICAgICAgICAgICAgICAgICAgICBwYXJlbnROYW1lID0gdGhpcy5tYXAucGFyZW50TWFwID8gdGhpcy5tYXAucGFyZW50TWFwLm5hbWUgOiBudWxsLAogICAgICAgICAgICAgICAgICAgICAgICBsb2NhbFJlcXVpcmUgPSBjb250ZXh0Lm1ha2VSZXF1aXJlKG1hcC5wYXJlbnRNYXAsIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVuYWJsZUJ1aWxkQ2FsbGJhY2s6IHRydWUKICAgICAgICAgICAgICAgICAgICAgICAgfSk7CgogICAgICAgICAgICAgICAgICAgIC8vSWYgY3VycmVudCBtYXAgaXMgbm90IG5vcm1hbGl6ZWQsIHdhaXQgZm9yIHRoYXQKICAgICAgICAgICAgICAgICAgICAvL25vcm1hbGl6ZWQgbmFtZSB0byBsb2FkIGluc3RlYWQgb2YgY29udGludWluZy4KICAgICAgICAgICAgICAgICAgICBpZiAodGhpcy5tYXAudW5ub3JtYWxpemVkKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIC8vTm9ybWFsaXplIHRoZSBJRCBpZiB0aGUgcGx1Z2luIGFsbG93cyBpdC4KICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHBsdWdpbi5ub3JtYWxpemUpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSBwbHVnaW4ubm9ybWFsaXplKG5hbWUsIGZ1bmN0aW9uIChuYW1lKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIG5vcm1hbGl6ZShuYW1lLCBwYXJlbnROYW1lLCB0cnVlKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0pIHx8ICcnOwogICAgICAgICAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgICAgICAgICAvL3ByZWZpeCBhbmQgbmFtZSBzaG91bGQgYWxyZWFkeSBiZSBub3JtYWxpemVkLCBubyBuZWVkCiAgICAgICAgICAgICAgICAgICAgICAgIC8vZm9yIGFwcGx5aW5nIG1hcCBjb25maWcgYWdhaW4gZWl0aGVyLgogICAgICAgICAgICAgICAgICAgICAgICBub3JtYWxpemVkTWFwID0gbWFrZU1vZHVsZU1hcChtYXAucHJlZml4ICsgJyEnICsgbmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5tYXAucGFyZW50TWFwKTsKICAgICAgICAgICAgICAgICAgICAgICAgb24obm9ybWFsaXplZE1hcCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICdkZWZpbmVkJywgYmluZCh0aGlzLCBmdW5jdGlvbiAodmFsdWUpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGlzLm1hcC5ub3JtYWxpemVkTWFwID0gbm9ybWFsaXplZE1hcDsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmluaXQoW10sIGZ1bmN0aW9uICgpIHsgcmV0dXJuIHZhbHVlOyB9LCBudWxsLCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVuYWJsZWQ6IHRydWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlnbm9yZTogdHJ1ZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0pOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgfSkpOwoKICAgICAgICAgICAgICAgICAgICAgICAgbm9ybWFsaXplZE1vZCA9IGdldE93bihyZWdpc3RyeSwgbm9ybWFsaXplZE1hcC5pZCk7CiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChub3JtYWxpemVkTW9kKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvL01hcmsgdGhpcyBhcyBhIGRlcGVuZGVuY3kgZm9yIHRoaXMgcGx1Z2luLCBzbyBpdAogICAgICAgICAgICAgICAgICAgICAgICAgICAgLy9jYW4gYmUgdHJhY2VkIGZvciBjeWNsZXMuCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmRlcE1hcHMucHVzaChub3JtYWxpemVkTWFwKTsKCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAodGhpcy5ldmVudHMuZXJyb3IpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBub3JtYWxpemVkTW9kLm9uKCdlcnJvcicsIGJpbmQodGhpcywgZnVuY3Rpb24gKGVycikgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmVtaXQoJ2Vycm9yJywgZXJyKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9KSk7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBub3JtYWxpemVkTW9kLmVuYWJsZSgpOwogICAgICAgICAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgICAgICAgICByZXR1cm47CiAgICAgICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgICAgICAvL0lmIGEgcGF0aHMgY29uZmlnLCB0aGVuIGp1c3QgbG9hZCB0aGF0IGZpbGUgaW5zdGVhZCB0bwogICAgICAgICAgICAgICAgICAgIC8vcmVzb2x2ZSB0aGUgcGx1Z2luLCBhcyBpdCBpcyBidWlsdCBpbnRvIHRoYXQgcGF0aHMgbGF5ZXIuCiAgICAgICAgICAgICAgICAgICAgaWYgKGJ1bmRsZUlkKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMubWFwLnVybCA9IGNvbnRleHQubmFtZVRvVXJsKGJ1bmRsZUlkKTsKICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5sb2FkKCk7CiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybjsKICAgICAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgICAgIGxvYWQgPSBiaW5kKHRoaXMsIGZ1bmN0aW9uICh2YWx1ZSkgewogICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmluaXQoW10sIGZ1bmN0aW9uICgpIHsgcmV0dXJuIHZhbHVlOyB9LCBudWxsLCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbmFibGVkOiB0cnVlCiAgICAgICAgICAgICAgICAgICAgICAgIH0pOwogICAgICAgICAgICAgICAgICAgIH0pOwoKICAgICAgICAgICAgICAgICAgICBsb2FkLmVycm9yID0gYmluZCh0aGlzLCBmdW5jdGlvbiAoZXJyKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuaW5pdGVkID0gdHJ1ZTsKICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5lcnJvciA9IGVycjsKICAgICAgICAgICAgICAgICAgICAgICAgZXJyLnJlcXVpcmVNb2R1bGVzID0gW2lkXTsKCiAgICAgICAgICAgICAgICAgICAgICAgIC8vUmVtb3ZlIHRlbXAgdW5ub3JtYWxpemVkIG1vZHVsZXMgZm9yIHRoaXMgbW9kdWxlLAogICAgICAgICAgICAgICAgICAgICAgICAvL3NpbmNlIHRoZXkgd2lsbCBuZXZlciBiZSByZXNvbHZlZCBvdGhlcndpc2Ugbm93LgogICAgICAgICAgICAgICAgICAgICAgICBlYWNoUHJvcChyZWdpc3RyeSwgZnVuY3Rpb24gKG1vZCkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKG1vZC5tYXAuaWQuaW5kZXhPZihpZCArICdfdW5ub3JtYWxpemVkJykgPT09IDApIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbGVhblJlZ2lzdHJ5KG1vZC5tYXAuaWQpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9KTsKCiAgICAgICAgICAgICAgICAgICAgICAgIG9uRXJyb3IoZXJyKTsKICAgICAgICAgICAgICAgICAgICB9KTsKCiAgICAgICAgICAgICAgICAgICAgLy9BbGxvdyBwbHVnaW5zIHRvIGxvYWQgb3RoZXIgY29kZSB3aXRob3V0IGhhdmluZyB0byBrbm93IHRoZQogICAgICAgICAgICAgICAgICAgIC8vY29udGV4dCBvciBob3cgdG8gJ2NvbXBsZXRlJyB0aGUgbG9hZC4KICAgICAgICAgICAgICAgICAgICBsb2FkLmZyb21UZXh0ID0gYmluZCh0aGlzLCBmdW5jdGlvbiAodGV4dCwgdGV4dEFsdCkgewogICAgICAgICAgICAgICAgICAgICAgICAvKmpzbGludCBldmlsOiB0cnVlICovCiAgICAgICAgICAgICAgICAgICAgICAgIHZhciBtb2R1bGVOYW1lID0gbWFwLm5hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtb2R1bGVNYXAgPSBtYWtlTW9kdWxlTWFwKG1vZHVsZU5hbWUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgaGFzSW50ZXJhY3RpdmUgPSB1c2VJbnRlcmFjdGl2ZTsKCiAgICAgICAgICAgICAgICAgICAgICAgIC8vQXMgb2YgMi4xLjAsIHN1cHBvcnQganVzdCBwYXNzaW5nIHRoZSB0ZXh0LCB0byByZWluZm9yY2UKICAgICAgICAgICAgICAgICAgICAgICAgLy9mcm9tVGV4dCBvbmx5IGJlaW5nIGNhbGxlZCBvbmNlIHBlciByZXNvdXJjZS4gU3RpbGwKICAgICAgICAgICAgICAgICAgICAgICAgLy9zdXBwb3J0IG9sZCBzdHlsZSBvZiBwYXNzaW5nIG1vZHVsZU5hbWUgYnV0IGRpc2NhcmQKICAgICAgICAgICAgICAgICAgICAgICAgLy90aGF0IG1vZHVsZU5hbWUgaW4gZmF2b3Igb2YgdGhlIGludGVybmFsIHJlZi4KICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHRleHRBbHQpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRleHQgPSB0ZXh0QWx0OwogICAgICAgICAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgICAgICAgICAvL1R1cm4gb2ZmIGludGVyYWN0aXZlIHNjcmlwdCBtYXRjaGluZyBmb3IgSUUgZm9yIGFueSBkZWZpbmUKICAgICAgICAgICAgICAgICAgICAgICAgLy9jYWxscyBpbiB0aGUgdGV4dCwgdGhlbiB0dXJuIGl0IGJhY2sgb24gYXQgdGhlIGVuZC4KICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGhhc0ludGVyYWN0aXZlKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB1c2VJbnRlcmFjdGl2ZSA9IGZhbHNlOwogICAgICAgICAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgICAgICAgICAvL1ByaW1lIHRoZSBzeXN0ZW0gYnkgY3JlYXRpbmcgYSBtb2R1bGUgaW5zdGFuY2UgZm9yCiAgICAgICAgICAgICAgICAgICAgICAgIC8vaXQuCiAgICAgICAgICAgICAgICAgICAgICAgIGdldE1vZHVsZShtb2R1bGVNYXApOwoKICAgICAgICAgICAgICAgICAgICAgICAgLy9UcmFuc2ZlciBhbnkgY29uZmlnIHRvIHRoaXMgb3RoZXIgbW9kdWxlLgogICAgICAgICAgICAgICAgICAgICAgICBpZiAoaGFzUHJvcChjb25maWcuY29uZmlnLCBpZCkpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbmZpZy5jb25maWdbbW9kdWxlTmFtZV0gPSBjb25maWcuY29uZmlnW2lkXTsKICAgICAgICAgICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgICAgICAgICAgdHJ5IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcS5leGVjKHRleHQpOwogICAgICAgICAgICAgICAgICAgICAgICB9IGNhdGNoIChlKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gb25FcnJvcihtYWtlRXJyb3IoJ2Zyb210ZXh0ZXZhbCcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdmcm9tVGV4dCBldmFsIGZvciAnICsgaWQgKwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICcgZmFpbGVkOiAnICsgZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgW2lkXSkpOwogICAgICAgICAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgICAgICAgICBpZiAoaGFzSW50ZXJhY3RpdmUpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVzZUludGVyYWN0aXZlID0gdHJ1ZTsKICAgICAgICAgICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgICAgICAgICAgLy9NYXJrIHRoaXMgYXMgYSBkZXBlbmRlbmN5IGZvciB0aGUgcGx1Z2luCiAgICAgICAgICAgICAgICAgICAgICAgIC8vcmVzb3VyY2UKICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5kZXBNYXBzLnB1c2gobW9kdWxlTWFwKTsKCiAgICAgICAgICAgICAgICAgICAgICAgIC8vU3VwcG9ydCBhbm9ueW1vdXMgbW9kdWxlcy4KICAgICAgICAgICAgICAgICAgICAgICAgY29udGV4dC5jb21wbGV0ZUxvYWQobW9kdWxlTmFtZSk7CgogICAgICAgICAgICAgICAgICAgICAgICAvL0JpbmQgdGhlIHZhbHVlIG9mIHRoYXQgbW9kdWxlIHRvIHRoZSB2YWx1ZSBmb3IgdGhpcwogICAgICAgICAgICAgICAgICAgICAgICAvL3Jlc291cmNlIElELgogICAgICAgICAgICAgICAgICAgICAgICBsb2NhbFJlcXVpcmUoW21vZHVsZU5hbWVdLCBsb2FkKTsKICAgICAgICAgICAgICAgICAgICB9KTsKCiAgICAgICAgICAgICAgICAgICAgLy9Vc2UgcGFyZW50TmFtZSBoZXJlIHNpbmNlIHRoZSBwbHVnaW4ncyBuYW1lIGlzIG5vdCByZWxpYWJsZSwKICAgICAgICAgICAgICAgICAgICAvL2NvdWxkIGJlIHNvbWUgd2VpcmQgc3RyaW5nIHdpdGggbm8gcGF0aCB0aGF0IGFjdHVhbGx5IHdhbnRzIHRvCiAgICAgICAgICAgICAgICAgICAgLy9yZWZlcmVuY2UgdGhlIHBhcmVudE5hbWUncyBwYXRoLgogICAgICAgICAgICAgICAgICAgIHBsdWdpbi5sb2FkKG1hcC5uYW1lLCBsb2NhbFJlcXVpcmUsIGxvYWQsIGNvbmZpZyk7CiAgICAgICAgICAgICAgICB9KSk7CgogICAgICAgICAgICAgICAgY29udGV4dC5lbmFibGUocGx1Z2luTWFwLCB0aGlzKTsKICAgICAgICAgICAgICAgIHRoaXMucGx1Z2luTWFwc1twbHVnaW5NYXAuaWRdID0gcGx1Z2luTWFwOwogICAgICAgICAgICB9LAoKICAgICAgICAgICAgZW5hYmxlOiBmdW5jdGlvbiAoKSB7CiAgICAgICAgICAgICAgICBlbmFibGVkUmVnaXN0cnlbdGhpcy5tYXAuaWRdID0gdGhpczsKICAgICAgICAgICAgICAgIHRoaXMuZW5hYmxlZCA9IHRydWU7CgogICAgICAgICAgICAgICAgLy9TZXQgZmxhZyBtZW50aW9uaW5nIHRoYXQgdGhlIG1vZHVsZSBpcyBlbmFibGluZywKICAgICAgICAgICAgICAgIC8vc28gdGhhdCBpbW1lZGlhdGUgY2FsbHMgdG8gdGhlIGRlZmluZWQgY2FsbGJhY2tzCiAgICAgICAgICAgICAgICAvL2ZvciBkZXBlbmRlbmNpZXMgZG8gbm90IHRyaWdnZXIgaW5hZHZlcnRlbnQgbG9hZAogICAgICAgICAgICAgICAgLy93aXRoIHRoZSBkZXBDb3VudCBzdGlsbCBiZWluZyB6ZXJvLgogICAgICAgICAgICAgICAgdGhpcy5lbmFibGluZyA9IHRydWU7CgogICAgICAgICAgICAgICAgLy9FbmFibGUgZWFjaCBkZXBlbmRlbmN5CiAgICAgICAgICAgICAgICBlYWNoKHRoaXMuZGVwTWFwcywgYmluZCh0aGlzLCBmdW5jdGlvbiAoZGVwTWFwLCBpKSB7CiAgICAgICAgICAgICAgICAgICAgdmFyIGlkLCBtb2QsIGhhbmRsZXI7CgogICAgICAgICAgICAgICAgICAgIGlmICh0eXBlb2YgZGVwTWFwID09PSAnc3RyaW5nJykgewogICAgICAgICAgICAgICAgICAgICAgICAvL0RlcGVuZGVuY3kgbmVlZHMgdG8gYmUgY29udmVydGVkIHRvIGEgZGVwTWFwCiAgICAgICAgICAgICAgICAgICAgICAgIC8vYW5kIHdpcmVkIHVwIHRvIHRoaXMgbW9kdWxlLgogICAgICAgICAgICAgICAgICAgICAgICBkZXBNYXAgPSBtYWtlTW9kdWxlTWFwKGRlcE1hcCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAodGhpcy5tYXAuaXNEZWZpbmUgPyB0aGlzLm1hcCA6IHRoaXMubWFwLnBhcmVudE1hcCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFsc2UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIXRoaXMuc2tpcE1hcCk7CiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuZGVwTWFwc1tpXSA9IGRlcE1hcDsKCiAgICAgICAgICAgICAgICAgICAgICAgIGhhbmRsZXIgPSBnZXRPd24oaGFuZGxlcnMsIGRlcE1hcC5pZCk7CgogICAgICAgICAgICAgICAgICAgICAgICBpZiAoaGFuZGxlcikgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5kZXBFeHBvcnRzW2ldID0gaGFuZGxlcih0aGlzKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybjsKICAgICAgICAgICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5kZXBDb3VudCArPSAxOwoKICAgICAgICAgICAgICAgICAgICAgICAgb24oZGVwTWFwLCAnZGVmaW5lZCcsIGJpbmQodGhpcywgZnVuY3Rpb24gKGRlcEV4cG9ydHMpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmICh0aGlzLnVuZGVmZWQpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm47CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmRlZmluZURlcChpLCBkZXBFeHBvcnRzKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuY2hlY2soKTsKICAgICAgICAgICAgICAgICAgICAgICAgfSkpOwoKICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHRoaXMuZXJyYmFjaykgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgb24oZGVwTWFwLCAnZXJyb3InLCBiaW5kKHRoaXMsIHRoaXMuZXJyYmFjaykpOwogICAgICAgICAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKHRoaXMuZXZlbnRzLmVycm9yKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBObyBkaXJlY3QgZXJyYmFjayBvbiB0aGlzIG1vZHVsZSwgYnV0IHNvbWV0aGluZwogICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gZWxzZSBpcyBsaXN0ZW5pbmcgZm9yIGVycm9ycywgc28gYmUgc3VyZSB0bwogICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gcHJvcGFnYXRlIHRoZSBlcnJvciBjb3JyZWN0bHkuCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBvbihkZXBNYXAsICdlcnJvcicsIGJpbmQodGhpcywgZnVuY3Rpb24oZXJyKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5lbWl0KCdlcnJvcicsIGVycik7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9KSk7CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgICAgIGlkID0gZGVwTWFwLmlkOwogICAgICAgICAgICAgICAgICAgIG1vZCA9IHJlZ2lzdHJ5W2lkXTsKCiAgICAgICAgICAgICAgICAgICAgLy9Ta2lwIHNwZWNpYWwgbW9kdWxlcyBsaWtlICdyZXF1aXJlJywgJ2V4cG9ydHMnLCAnbW9kdWxlJwogICAgICAgICAgICAgICAgICAgIC8vQWxzbywgZG9uJ3QgY2FsbCBlbmFibGUgaWYgaXQgaXMgYWxyZWFkeSBlbmFibGVkLAogICAgICAgICAgICAgICAgICAgIC8vaW1wb3J0YW50IGluIGNpcmN1bGFyIGRlcGVuZGVuY3kgY2FzZXMuCiAgICAgICAgICAgICAgICAgICAgaWYgKCFoYXNQcm9wKGhhbmRsZXJzLCBpZCkgJiYgbW9kICYmICFtb2QuZW5hYmxlZCkgewogICAgICAgICAgICAgICAgICAgICAgICBjb250ZXh0LmVuYWJsZShkZXBNYXAsIHRoaXMpOwogICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIH0pKTsKCiAgICAgICAgICAgICAgICAvL0VuYWJsZSBlYWNoIHBsdWdpbiB0aGF0IGlzIHVzZWQgaW4KICAgICAgICAgICAgICAgIC8vYSBkZXBlbmRlbmN5CiAgICAgICAgICAgICAgICBlYWNoUHJvcCh0aGlzLnBsdWdpbk1hcHMsIGJpbmQodGhpcywgZnVuY3Rpb24gKHBsdWdpbk1hcCkgewogICAgICAgICAgICAgICAgICAgIHZhciBtb2QgPSBnZXRPd24ocmVnaXN0cnksIHBsdWdpbk1hcC5pZCk7CiAgICAgICAgICAgICAgICAgICAgaWYgKG1vZCAmJiAhbW9kLmVuYWJsZWQpIHsKICAgICAgICAgICAgICAgICAgICAgICAgY29udGV4dC5lbmFibGUocGx1Z2luTWFwLCB0aGlzKTsKICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICB9KSk7CgogICAgICAgICAgICAgICAgdGhpcy5lbmFibGluZyA9IGZhbHNlOwoKICAgICAgICAgICAgICAgIHRoaXMuY2hlY2soKTsKICAgICAgICAgICAgfSwKCiAgICAgICAgICAgIG9uOiBmdW5jdGlvbiAobmFtZSwgY2IpIHsKICAgICAgICAgICAgICAgIHZhciBjYnMgPSB0aGlzLmV2ZW50c1tuYW1lXTsKICAgICAgICAgICAgICAgIGlmICghY2JzKSB7CiAgICAgICAgICAgICAgICAgICAgY2JzID0gdGhpcy5ldmVudHNbbmFtZV0gPSBbXTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIGNicy5wdXNoKGNiKTsKICAgICAgICAgICAgfSwKCiAgICAgICAgICAgIGVtaXQ6IGZ1bmN0aW9uIChuYW1lLCBldnQpIHsKICAgICAgICAgICAgICAgIGVhY2godGhpcy5ldmVudHNbbmFtZV0sIGZ1bmN0aW9uIChjYikgewogICAgICAgICAgICAgICAgICAgIGNiKGV2dCk7CiAgICAgICAgICAgICAgICB9KTsKICAgICAgICAgICAgICAgIGlmIChuYW1lID09PSAnZXJyb3InKSB7CiAgICAgICAgICAgICAgICAgICAgLy9Ob3cgdGhhdCB0aGUgZXJyb3IgaGFuZGxlciB3YXMgdHJpZ2dlcmVkLCByZW1vdmUKICAgICAgICAgICAgICAgICAgICAvL3RoZSBsaXN0ZW5lcnMsIHNpbmNlIHRoaXMgYnJva2VuIE1vZHVsZSBpbnN0YW5jZQogICAgICAgICAgICAgICAgICAgIC8vY2FuIHN0YXkgYXJvdW5kIGZvciBhIHdoaWxlIGluIHRoZSByZWdpc3RyeS4KICAgICAgICAgICAgICAgICAgICBkZWxldGUgdGhpcy5ldmVudHNbbmFtZV07CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICB9OwoKICAgICAgICBmdW5jdGlvbiBjYWxsR2V0TW9kdWxlKGFyZ3MpIHsKICAgICAgICAgICAgLy9Ta2lwIG1vZHVsZXMgYWxyZWFkeSBkZWZpbmVkLgogICAgICAgICAgICBpZiAoIWhhc1Byb3AoZGVmaW5lZCwgYXJnc1swXSkpIHsKICAgICAgICAgICAgICAgIGdldE1vZHVsZShtYWtlTW9kdWxlTWFwKGFyZ3NbMF0sIG51bGwsIHRydWUpKS5pbml0KGFyZ3NbMV0sIGFyZ3NbMl0pOwogICAgICAgICAgICB9CiAgICAgICAgfQoKICAgICAgICBmdW5jdGlvbiByZW1vdmVMaXN0ZW5lcihub2RlLCBmdW5jLCBuYW1lLCBpZU5hbWUpIHsKICAgICAgICAgICAgLy9GYXZvciBkZXRhY2hFdmVudCBiZWNhdXNlIG9mIElFOQogICAgICAgICAgICAvL2lzc3VlLCBzZWUgYXR0YWNoRXZlbnQvYWRkRXZlbnRMaXN0ZW5lciBjb21tZW50IGVsc2V3aGVyZQogICAgICAgICAgICAvL2luIHRoaXMgZmlsZS4KICAgICAgICAgICAgaWYgKG5vZGUuZGV0YWNoRXZlbnQgJiYgIWlzT3BlcmEpIHsKICAgICAgICAgICAgICAgIC8vUHJvYmFibHkgSUUuIElmIG5vdCBpdCB3aWxsIHRocm93IGFuIGVycm9yLCB3aGljaCB3aWxsIGJlCiAgICAgICAgICAgICAgICAvL3VzZWZ1bCB0byBrbm93LgogICAgICAgICAgICAgICAgaWYgKGllTmFtZSkgewogICAgICAgICAgICAgICAgICAgIG5vZGUuZGV0YWNoRXZlbnQoaWVOYW1lLCBmdW5jKTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgICAgIG5vZGUucmVtb3ZlRXZlbnRMaXN0ZW5lcihuYW1lLCBmdW5jLCBmYWxzZSk7CiAgICAgICAgICAgIH0KICAgICAgICB9CgogICAgICAgIC8qKgogICAgICAgICAqIEdpdmVuIGFuIGV2ZW50IGZyb20gYSBzY3JpcHQgbm9kZSwgZ2V0IHRoZSByZXF1aXJlanMgaW5mbyBmcm9tIGl0LAogICAgICAgICAqIGFuZCB0aGVuIHJlbW92ZXMgdGhlIGV2ZW50IGxpc3RlbmVycyBvbiB0aGUgbm9kZS4KICAgICAgICAgKiBAcGFyYW0ge0V2ZW50fSBldnQKICAgICAgICAgKiBAcmV0dXJucyB7T2JqZWN0fQogICAgICAgICAqLwogICAgICAgIGZ1bmN0aW9uIGdldFNjcmlwdERhdGEoZXZ0KSB7CiAgICAgICAgICAgIC8vVXNpbmcgY3VycmVudFRhcmdldCBpbnN0ZWFkIG9mIHRhcmdldCBmb3IgRmlyZWZveCAyLjAncyBzYWtlLiBOb3QKICAgICAgICAgICAgLy9hbGwgb2xkIGJyb3dzZXJzIHdpbGwgYmUgc3VwcG9ydGVkLCBidXQgdGhpcyBvbmUgd2FzIGVhc3kgZW5vdWdoCiAgICAgICAgICAgIC8vdG8gc3VwcG9ydCBhbmQgc3RpbGwgbWFrZXMgc2Vuc2UuCiAgICAgICAgICAgIHZhciBub2RlID0gZXZ0LmN1cnJlbnRUYXJnZXQgfHwgZXZ0LnNyY0VsZW1lbnQ7CgogICAgICAgICAgICAvL1JlbW92ZSB0aGUgbGlzdGVuZXJzIG9uY2UgaGVyZS4KICAgICAgICAgICAgcmVtb3ZlTGlzdGVuZXIobm9kZSwgY29udGV4dC5vblNjcmlwdExvYWQsICdsb2FkJywgJ29ucmVhZHlzdGF0ZWNoYW5nZScpOwogICAgICAgICAgICByZW1vdmVMaXN0ZW5lcihub2RlLCBjb250ZXh0Lm9uU2NyaXB0RXJyb3IsICdlcnJvcicpOwoKICAgICAgICAgICAgcmV0dXJuIHsKICAgICAgICAgICAgICAgIG5vZGU6IG5vZGUsCiAgICAgICAgICAgICAgICBpZDogbm9kZSAmJiBub2RlLmdldEF0dHJpYnV0ZSgnZGF0YS1yZXF1aXJlbW9kdWxlJykKICAgICAgICAgICAgfTsKICAgICAgICB9CgogICAgICAgIGZ1bmN0aW9uIGludGFrZURlZmluZXMoKSB7CiAgICAgICAgICAgIHZhciBhcmdzOwoKICAgICAgICAgICAgLy9BbnkgZGVmaW5lZCBtb2R1bGVzIGluIHRoZSBnbG9iYWwgcXVldWUsIGludGFrZSB0aGVtIG5vdy4KICAgICAgICAgICAgdGFrZUdsb2JhbFF1ZXVlKCk7CgogICAgICAgICAgICAvL01ha2Ugc3VyZSBhbnkgcmVtYWluaW5nIGRlZlF1ZXVlIGl0ZW1zIGdldCBwcm9wZXJseSBwcm9jZXNzZWQuCiAgICAgICAgICAgIHdoaWxlIChkZWZRdWV1ZS5sZW5ndGgpIHsKICAgICAgICAgICAgICAgIGFyZ3MgPSBkZWZRdWV1ZS5zaGlmdCgpOwogICAgICAgICAgICAgICAgaWYgKGFyZ3NbMF0gPT09IG51bGwpIHsKICAgICAgICAgICAgICAgICAgICByZXR1cm4gb25FcnJvcihtYWtlRXJyb3IoJ21pc21hdGNoJywgJ01pc21hdGNoZWQgYW5vbnltb3VzIGRlZmluZSgpIG1vZHVsZTogJyArCiAgICAgICAgICAgICAgICAgICAgICAgIGFyZ3NbYXJncy5sZW5ndGggLSAxXSkpOwogICAgICAgICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgICAgICAgICAvL2FyZ3MgYXJlIGlkLCBkZXBzLCBmYWN0b3J5LiBTaG91bGQgYmUgbm9ybWFsaXplZCBieSB0aGUKICAgICAgICAgICAgICAgICAgICAvL2RlZmluZSgpIGZ1bmN0aW9uLgogICAgICAgICAgICAgICAgICAgIGNhbGxHZXRNb2R1bGUoYXJncyk7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICAgICAgY29udGV4dC5kZWZRdWV1ZU1hcCA9IHt9OwogICAgICAgIH0KCiAgICAgICAgY29udGV4dCA9IHsKICAgICAgICAgICAgY29uZmlnOiBjb25maWcsCiAgICAgICAgICAgIGNvbnRleHROYW1lOiBjb250ZXh0TmFtZSwKICAgICAgICAgICAgcmVnaXN0cnk6IHJlZ2lzdHJ5LAogICAgICAgICAgICBkZWZpbmVkOiBkZWZpbmVkLAogICAgICAgICAgICB1cmxGZXRjaGVkOiB1cmxGZXRjaGVkLAogICAgICAgICAgICBkZWZRdWV1ZTogZGVmUXVldWUsCiAgICAgICAgICAgIGRlZlF1ZXVlTWFwOiB7fSwKICAgICAgICAgICAgTW9kdWxlOiBNb2R1bGUsCiAgICAgICAgICAgIG1ha2VNb2R1bGVNYXA6IG1ha2VNb2R1bGVNYXAsCiAgICAgICAgICAgIG5leHRUaWNrOiByZXEubmV4dFRpY2ssCiAgICAgICAgICAgIG9uRXJyb3I6IG9uRXJyb3IsCgogICAgICAgICAgICAvKioKICAgICAgICAgICAgICogU2V0IGEgY29uZmlndXJhdGlvbiBmb3IgdGhlIGNvbnRleHQuCiAgICAgICAgICAgICAqIEBwYXJhbSB7T2JqZWN0fSBjZmcgY29uZmlnIG9iamVjdCB0byBpbnRlZ3JhdGUuCiAgICAgICAgICAgICAqLwogICAgICAgICAgICBjb25maWd1cmU6IGZ1bmN0aW9uIChjZmcpIHsKICAgICAgICAgICAgICAgIC8vTWFrZSBzdXJlIHRoZSBiYXNlVXJsIGVuZHMgaW4gYSBzbGFzaC4KICAgICAgICAgICAgICAgIGlmIChjZmcuYmFzZVVybCkgewogICAgICAgICAgICAgICAgICAgIGlmIChjZmcuYmFzZVVybC5jaGFyQXQoY2ZnLmJhc2VVcmwubGVuZ3RoIC0gMSkgIT09ICcvJykgewogICAgICAgICAgICAgICAgICAgICAgICBjZmcuYmFzZVVybCArPSAnLyc7CiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgIC8vU2F2ZSBvZmYgdGhlIHBhdGhzIHNpbmNlIHRoZXkgcmVxdWlyZSBzcGVjaWFsIHByb2Nlc3NpbmcsCiAgICAgICAgICAgICAgICAvL3RoZXkgYXJlIGFkZGl0aXZlLgogICAgICAgICAgICAgICAgdmFyIHNoaW0gPSBjb25maWcuc2hpbSwKICAgICAgICAgICAgICAgICAgICBvYmpzID0gewogICAgICAgICAgICAgICAgICAgICAgICBwYXRoczogdHJ1ZSwKICAgICAgICAgICAgICAgICAgICAgICAgYnVuZGxlczogdHJ1ZSwKICAgICAgICAgICAgICAgICAgICAgICAgY29uZmlnOiB0cnVlLAogICAgICAgICAgICAgICAgICAgICAgICBtYXA6IHRydWUKICAgICAgICAgICAgICAgICAgICB9OwoKICAgICAgICAgICAgICAgIGVhY2hQcm9wKGNmZywgZnVuY3Rpb24gKHZhbHVlLCBwcm9wKSB7CiAgICAgICAgICAgICAgICAgICAgaWYgKG9ianNbcHJvcF0pIHsKICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCFjb25maWdbcHJvcF0pIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbmZpZ1twcm9wXSA9IHt9OwogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIG1peGluKGNvbmZpZ1twcm9wXSwgdmFsdWUsIHRydWUsIHRydWUpOwogICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgICAgICAgICAgICAgIGNvbmZpZ1twcm9wXSA9IHZhbHVlOwogICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIH0pOwoKICAgICAgICAgICAgICAgIC8vUmV2ZXJzZSBtYXAgdGhlIGJ1bmRsZXMKICAgICAgICAgICAgICAgIGlmIChjZmcuYnVuZGxlcykgewogICAgICAgICAgICAgICAgICAgIGVhY2hQcm9wKGNmZy5idW5kbGVzLCBmdW5jdGlvbiAodmFsdWUsIHByb3ApIHsKICAgICAgICAgICAgICAgICAgICAgICAgZWFjaCh2YWx1ZSwgZnVuY3Rpb24gKHYpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmICh2ICE9PSBwcm9wKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnVuZGxlc01hcFt2XSA9IHByb3A7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0pOwogICAgICAgICAgICAgICAgICAgIH0pOwogICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgIC8vTWVyZ2Ugc2hpbQogICAgICAgICAgICAgICAgaWYgKGNmZy5zaGltKSB7CiAgICAgICAgICAgICAgICAgICAgZWFjaFByb3AoY2ZnLnNoaW0sIGZ1bmN0aW9uICh2YWx1ZSwgaWQpIHsKICAgICAgICAgICAgICAgICAgICAgICAgLy9Ob3JtYWxpemUgdGhlIHN0cnVjdHVyZQogICAgICAgICAgICAgICAgICAgICAgICBpZiAoaXNBcnJheSh2YWx1ZSkpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlID0gewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlcHM6IHZhbHVlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9OwogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIGlmICgodmFsdWUuZXhwb3J0cyB8fCB2YWx1ZS5pbml0KSAmJiAhdmFsdWUuZXhwb3J0c0ZuKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZS5leHBvcnRzRm4gPSBjb250ZXh0Lm1ha2VTaGltRXhwb3J0cyh2YWx1ZSk7CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgc2hpbVtpZF0gPSB2YWx1ZTsKICAgICAgICAgICAgICAgICAgICB9KTsKICAgICAgICAgICAgICAgICAgICBjb25maWcuc2hpbSA9IHNoaW07CiAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgLy9BZGp1c3QgcGFja2FnZXMgaWYgbmVjZXNzYXJ5LgogICAgICAgICAgICAgICAgaWYgKGNmZy5wYWNrYWdlcykgewogICAgICAgICAgICAgICAgICAgIGVhY2goY2ZnLnBhY2thZ2VzLCBmdW5jdGlvbiAocGtnT2JqKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIHZhciBsb2NhdGlvbiwgbmFtZTsKCiAgICAgICAgICAgICAgICAgICAgICAgIHBrZ09iaiA9IHR5cGVvZiBwa2dPYmogPT09ICdzdHJpbmcnID8ge25hbWU6IHBrZ09ian0gOiBwa2dPYmo7CgogICAgICAgICAgICAgICAgICAgICAgICBuYW1lID0gcGtnT2JqLm5hbWU7CiAgICAgICAgICAgICAgICAgICAgICAgIGxvY2F0aW9uID0gcGtnT2JqLmxvY2F0aW9uOwogICAgICAgICAgICAgICAgICAgICAgICBpZiAobG9jYXRpb24pIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbmZpZy5wYXRoc1tuYW1lXSA9IHBrZ09iai5sb2NhdGlvbjsKICAgICAgICAgICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgICAgICAgICAgLy9TYXZlIHBvaW50ZXIgdG8gbWFpbiBtb2R1bGUgSUQgZm9yIHBrZyBuYW1lLgogICAgICAgICAgICAgICAgICAgICAgICAvL1JlbW92ZSBsZWFkaW5nIGRvdCBpbiBtYWluLCBzbyBtYWluIHBhdGhzIGFyZSBub3JtYWxpemVkLAogICAgICAgICAgICAgICAgICAgICAgICAvL2FuZCByZW1vdmUgYW55IHRyYWlsaW5nIC5qcywgc2luY2UgZGlmZmVyZW50IHBhY2thZ2UKICAgICAgICAgICAgICAgICAgICAgICAgLy9lbnZzIGhhdmUgZGlmZmVyZW50IGNvbnZlbnRpb25zOiBzb21lIHVzZSBhIG1vZHVsZSBuYW1lLAogICAgICAgICAgICAgICAgICAgICAgICAvL3NvbWUgdXNlIGEgZmlsZSBuYW1lLgogICAgICAgICAgICAgICAgICAgICAgICBjb25maWcucGtnc1tuYW1lXSA9IHBrZ09iai5uYW1lICsgJy8nICsgKHBrZ09iai5tYWluIHx8ICdtYWluJykKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC5yZXBsYWNlKGN1cnJEaXJSZWdFeHAsICcnKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLnJlcGxhY2UoanNTdWZmaXhSZWdFeHAsICcnKTsKICAgICAgICAgICAgICAgICAgICB9KTsKICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICAvL0lmIHRoZXJlIGFyZSBhbnkgIndhaXRpbmcgdG8gZXhlY3V0ZSIgbW9kdWxlcyBpbiB0aGUgcmVnaXN0cnksCiAgICAgICAgICAgICAgICAvL3VwZGF0ZSB0aGUgbWFwcyBmb3IgdGhlbSwgc2luY2UgdGhlaXIgaW5mbywgbGlrZSBVUkxzIHRvIGxvYWQsCiAgICAgICAgICAgICAgICAvL21heSBoYXZlIGNoYW5nZWQuCiAgICAgICAgICAgICAgICBlYWNoUHJvcChyZWdpc3RyeSwgZnVuY3Rpb24gKG1vZCwgaWQpIHsKICAgICAgICAgICAgICAgICAgICAvL0lmIG1vZHVsZSBhbHJlYWR5IGhhcyBpbml0IGNhbGxlZCwgc2luY2UgaXQgaXMgdG9vCiAgICAgICAgICAgICAgICAgICAgLy9sYXRlIHRvIG1vZGlmeSB0aGVtLCBhbmQgaWdub3JlIHVubm9ybWFsaXplZCBvbmVzCiAgICAgICAgICAgICAgICAgICAgLy9zaW5jZSB0aGV5IGFyZSB0cmFuc2llbnQuCiAgICAgICAgICAgICAgICAgICAgaWYgKCFtb2QuaW5pdGVkICYmICFtb2QubWFwLnVubm9ybWFsaXplZCkgewogICAgICAgICAgICAgICAgICAgICAgICBtb2QubWFwID0gbWFrZU1vZHVsZU1hcChpZCwgbnVsbCwgdHJ1ZSk7CiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfSk7CgogICAgICAgICAgICAgICAgLy9JZiBhIGRlcHMgYXJyYXkgb3IgYSBjb25maWcgY2FsbGJhY2sgaXMgc3BlY2lmaWVkLCB0aGVuIGNhbGwKICAgICAgICAgICAgICAgIC8vcmVxdWlyZSB3aXRoIHRob3NlIGFyZ3MuIFRoaXMgaXMgdXNlZnVsIHdoZW4gcmVxdWlyZSBpcyBkZWZpbmVkIGFzIGEKICAgICAgICAgICAgICAgIC8vY29uZmlnIG9iamVjdCBiZWZvcmUgcmVxdWlyZS5qcyBpcyBsb2FkZWQuCiAgICAgICAgICAgICAgICBpZiAoY2ZnLmRlcHMgfHwgY2ZnLmNhbGxiYWNrKSB7CiAgICAgICAgICAgICAgICAgICAgY29udGV4dC5yZXF1aXJlKGNmZy5kZXBzIHx8IFtdLCBjZmcuY2FsbGJhY2spOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAoKICAgICAgICAgICAgbWFrZVNoaW1FeHBvcnRzOiBmdW5jdGlvbiAodmFsdWUpIHsKICAgICAgICAgICAgICAgIGZ1bmN0aW9uIGZuKCkgewogICAgICAgICAgICAgICAgICAgIHZhciByZXQ7CiAgICAgICAgICAgICAgICAgICAgaWYgKHZhbHVlLmluaXQpIHsKICAgICAgICAgICAgICAgICAgICAgICAgcmV0ID0gdmFsdWUuaW5pdC5hcHBseShnbG9iYWwsIGFyZ3VtZW50cyk7CiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgIHJldHVybiByZXQgfHwgKHZhbHVlLmV4cG9ydHMgJiYgZ2V0R2xvYmFsKHZhbHVlLmV4cG9ydHMpKTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIHJldHVybiBmbjsKICAgICAgICAgICAgfSwKCiAgICAgICAgICAgIG1ha2VSZXF1aXJlOiBmdW5jdGlvbiAocmVsTWFwLCBvcHRpb25zKSB7CiAgICAgICAgICAgICAgICBvcHRpb25zID0gb3B0aW9ucyB8fCB7fTsKCiAgICAgICAgICAgICAgICBmdW5jdGlvbiBsb2NhbFJlcXVpcmUoZGVwcywgY2FsbGJhY2ssIGVycmJhY2spIHsKICAgICAgICAgICAgICAgICAgICB2YXIgaWQsIG1hcCwgcmVxdWlyZU1vZDsKCiAgICAgICAgICAgICAgICAgICAgaWYgKG9wdGlvbnMuZW5hYmxlQnVpbGRDYWxsYmFjayAmJiBjYWxsYmFjayAmJiBpc0Z1bmN0aW9uKGNhbGxiYWNrKSkgewogICAgICAgICAgICAgICAgICAgICAgICBjYWxsYmFjay5fX3JlcXVpcmVKc0J1aWxkID0gdHJ1ZTsKICAgICAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgICAgIGlmICh0eXBlb2YgZGVwcyA9PT0gJ3N0cmluZycpIHsKICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGlzRnVuY3Rpb24oY2FsbGJhY2spKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvL0ludmFsaWQgY2FsbAogICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIG9uRXJyb3IobWFrZUVycm9yKCdyZXF1aXJlYXJncycsICdJbnZhbGlkIHJlcXVpcmUgY2FsbCcpLCBlcnJiYWNrKTsKICAgICAgICAgICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgICAgICAgICAgLy9JZiByZXF1aXJlfGV4cG9ydHN8bW9kdWxlIGFyZSByZXF1ZXN0ZWQsIGdldCB0aGUKICAgICAgICAgICAgICAgICAgICAgICAgLy92YWx1ZSBmb3IgdGhlbSBmcm9tIHRoZSBzcGVjaWFsIGhhbmRsZXJzLiBDYXZlYXQ6CiAgICAgICAgICAgICAgICAgICAgICAgIC8vdGhpcyBvbmx5IHdvcmtzIHdoaWxlIG1vZHVsZSBpcyBiZWluZyBkZWZpbmVkLgogICAgICAgICAgICAgICAgICAgICAgICBpZiAocmVsTWFwICYmIGhhc1Byb3AoaGFuZGxlcnMsIGRlcHMpKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gaGFuZGxlcnNbZGVwc10ocmVnaXN0cnlbcmVsTWFwLmlkXSk7CiAgICAgICAgICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICAgICAgICAgIC8vU3luY2hyb25vdXMgYWNjZXNzIHRvIG9uZSBtb2R1bGUuIElmIHJlcXVpcmUuZ2V0IGlzCiAgICAgICAgICAgICAgICAgICAgICAgIC8vYXZhaWxhYmxlIChhcyBpbiB0aGUgTm9kZSBhZGFwdGVyKSwgcHJlZmVyIHRoYXQuCiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChyZXEuZ2V0KSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gcmVxLmdldChjb250ZXh0LCBkZXBzLCByZWxNYXAsIGxvY2FsUmVxdWlyZSk7CiAgICAgICAgICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICAgICAgICAgIC8vTm9ybWFsaXplIG1vZHVsZSBuYW1lLCBpZiBpdCBjb250YWlucyAuIG9yIC4uCiAgICAgICAgICAgICAgICAgICAgICAgIG1hcCA9IG1ha2VNb2R1bGVNYXAoZGVwcywgcmVsTWFwLCBmYWxzZSwgdHJ1ZSk7CiAgICAgICAgICAgICAgICAgICAgICAgIGlkID0gbWFwLmlkOwoKICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCFoYXNQcm9wKGRlZmluZWQsIGlkKSkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIG9uRXJyb3IobWFrZUVycm9yKCdub3Rsb2FkZWQnLCAnTW9kdWxlIG5hbWUgIicgKwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWQgKwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJyIgaGFzIG5vdCBiZWVuIGxvYWRlZCB5ZXQgZm9yIGNvbnRleHQ6ICcgKwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29udGV4dE5hbWUgKwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKHJlbE1hcCA/ICcnIDogJy4gVXNlIHJlcXVpcmUoW10pJykpKTsKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gZGVmaW5lZFtpZF07CiAgICAgICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgICAgICAvL0dyYWIgZGVmaW5lcyB3YWl0aW5nIGluIHRoZSBnbG9iYWwgcXVldWUuCiAgICAgICAgICAgICAgICAgICAgaW50YWtlRGVmaW5lcygpOwoKICAgICAgICAgICAgICAgICAgICAvL01hcmsgYWxsIHRoZSBkZXBlbmRlbmNpZXMgYXMgbmVlZGluZyB0byBiZSBsb2FkZWQuCiAgICAgICAgICAgICAgICAgICAgY29udGV4dC5uZXh0VGljayhmdW5jdGlvbiAoKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIC8vU29tZSBkZWZpbmVzIGNvdWxkIGhhdmUgYmVlbiBhZGRlZCBzaW5jZSB0aGUKICAgICAgICAgICAgICAgICAgICAgICAgLy9yZXF1aXJlIGNhbGwsIGNvbGxlY3QgdGhlbS4KICAgICAgICAgICAgICAgICAgICAgICAgaW50YWtlRGVmaW5lcygpOwoKICAgICAgICAgICAgICAgICAgICAgICAgcmVxdWlyZU1vZCA9IGdldE1vZHVsZShtYWtlTW9kdWxlTWFwKG51bGwsIHJlbE1hcCkpOwoKICAgICAgICAgICAgICAgICAgICAgICAgLy9TdG9yZSBpZiBtYXAgY29uZmlnIHNob3VsZCBiZSBhcHBsaWVkIHRvIHRoaXMgcmVxdWlyZQogICAgICAgICAgICAgICAgICAgICAgICAvL2NhbGwgZm9yIGRlcGVuZGVuY2llcy4KICAgICAgICAgICAgICAgICAgICAgICAgcmVxdWlyZU1vZC5za2lwTWFwID0gb3B0aW9ucy5za2lwTWFwOwoKICAgICAgICAgICAgICAgICAgICAgICAgcmVxdWlyZU1vZC5pbml0KGRlcHMsIGNhbGxiYWNrLCBlcnJiYWNrLCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbmFibGVkOiB0cnVlCiAgICAgICAgICAgICAgICAgICAgICAgIH0pOwoKICAgICAgICAgICAgICAgICAgICAgICAgY2hlY2tMb2FkZWQoKTsKICAgICAgICAgICAgICAgICAgICB9KTsKCiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGxvY2FsUmVxdWlyZTsKICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICBtaXhpbihsb2NhbFJlcXVpcmUsIHsKICAgICAgICAgICAgICAgICAgICBpc0Jyb3dzZXI6IGlzQnJvd3NlciwKCiAgICAgICAgICAgICAgICAgICAgLyoqCiAgICAgICAgICAgICAgICAgICAgICogQ29udmVydHMgYSBtb2R1bGUgbmFtZSArIC5leHRlbnNpb24gaW50byBhbiBVUkwgcGF0aC4KICAgICAgICAgICAgICAgICAgICAgKiAqUmVxdWlyZXMqIHRoZSB1c2Ugb2YgYSBtb2R1bGUgbmFtZS4gSXQgZG9lcyBub3Qgc3VwcG9ydCB1c2luZwogICAgICAgICAgICAgICAgICAgICAqIHBsYWluIFVSTHMgbGlrZSBuYW1lVG9VcmwuCiAgICAgICAgICAgICAgICAgICAgICovCiAgICAgICAgICAgICAgICAgICAgdG9Vcmw6IGZ1bmN0aW9uIChtb2R1bGVOYW1lUGx1c0V4dCkgewogICAgICAgICAgICAgICAgICAgICAgICB2YXIgZXh0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5kZXggPSBtb2R1bGVOYW1lUGx1c0V4dC5sYXN0SW5kZXhPZignLicpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VnbWVudCA9IG1vZHVsZU5hbWVQbHVzRXh0LnNwbGl0KCcvJylbMF0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpc1JlbGF0aXZlID0gc2VnbWVudCA9PT0gJy4nIHx8IHNlZ21lbnQgPT09ICcuLic7CgogICAgICAgICAgICAgICAgICAgICAgICAvL0hhdmUgYSBmaWxlIGV4dGVuc2lvbiBhbGlhcywgYW5kIGl0IGlzIG5vdCB0aGUKICAgICAgICAgICAgICAgICAgICAgICAgLy9kb3RzIGZyb20gYSByZWxhdGl2ZSBwYXRoLgogICAgICAgICAgICAgICAgICAgICAgICBpZiAoaW5kZXggIT09IC0xICYmICghaXNSZWxhdGl2ZSB8fCBpbmRleCA+IDEpKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBleHQgPSBtb2R1bGVOYW1lUGx1c0V4dC5zdWJzdHJpbmcoaW5kZXgsIG1vZHVsZU5hbWVQbHVzRXh0Lmxlbmd0aCk7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtb2R1bGVOYW1lUGx1c0V4dCA9IG1vZHVsZU5hbWVQbHVzRXh0LnN1YnN0cmluZygwLCBpbmRleCk7CiAgICAgICAgICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiBjb250ZXh0Lm5hbWVUb1VybChub3JtYWxpemUobW9kdWxlTmFtZVBsdXNFeHQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlbE1hcCAmJiByZWxNYXAuaWQsIHRydWUpLCBleHQsICB0cnVlKTsKICAgICAgICAgICAgICAgICAgICB9LAoKICAgICAgICAgICAgICAgICAgICBkZWZpbmVkOiBmdW5jdGlvbiAoaWQpIHsKICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGhhc1Byb3AoZGVmaW5lZCwgbWFrZU1vZHVsZU1hcChpZCwgcmVsTWFwLCBmYWxzZSwgdHJ1ZSkuaWQpOwogICAgICAgICAgICAgICAgICAgIH0sCgogICAgICAgICAgICAgICAgICAgIHNwZWNpZmllZDogZnVuY3Rpb24gKGlkKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIGlkID0gbWFrZU1vZHVsZU1hcChpZCwgcmVsTWFwLCBmYWxzZSwgdHJ1ZSkuaWQ7CiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiBoYXNQcm9wKGRlZmluZWQsIGlkKSB8fCBoYXNQcm9wKHJlZ2lzdHJ5LCBpZCk7CiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfSk7CgogICAgICAgICAgICAgICAgLy9Pbmx5IGFsbG93IHVuZGVmIG9uIHRvcCBsZXZlbCByZXF1aXJlIGNhbGxzCiAgICAgICAgICAgICAgICBpZiAoIXJlbE1hcCkgewogICAgICAgICAgICAgICAgICAgIGxvY2FsUmVxdWlyZS51bmRlZiA9IGZ1bmN0aW9uIChpZCkgewogICAgICAgICAgICAgICAgICAgICAgICAvL0JpbmQgYW55IHdhaXRpbmcgZGVmaW5lKCkgY2FsbHMgdG8gdGhpcyBjb250ZXh0LAogICAgICAgICAgICAgICAgICAgICAgICAvL2ZpeCBmb3IgIzQwOAogICAgICAgICAgICAgICAgICAgICAgICB0YWtlR2xvYmFsUXVldWUoKTsKCiAgICAgICAgICAgICAgICAgICAgICAgIHZhciBtYXAgPSBtYWtlTW9kdWxlTWFwKGlkLCByZWxNYXAsIHRydWUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9kID0gZ2V0T3duKHJlZ2lzdHJ5LCBpZCk7CgogICAgICAgICAgICAgICAgICAgICAgICBtb2QudW5kZWZlZCA9IHRydWU7CiAgICAgICAgICAgICAgICAgICAgICAgIHJlbW92ZVNjcmlwdChpZCk7CgogICAgICAgICAgICAgICAgICAgICAgICBkZWxldGUgZGVmaW5lZFtpZF07CiAgICAgICAgICAgICAgICAgICAgICAgIGRlbGV0ZSB1cmxGZXRjaGVkW21hcC51cmxdOwogICAgICAgICAgICAgICAgICAgICAgICBkZWxldGUgdW5kZWZFdmVudHNbaWRdOwoKICAgICAgICAgICAgICAgICAgICAgICAgLy9DbGVhbiBxdWV1ZWQgZGVmaW5lcyB0b28uIEdvIGJhY2t3YXJkcwogICAgICAgICAgICAgICAgICAgICAgICAvL2luIGFycmF5IHNvIHRoYXQgdGhlIHNwbGljZXMgZG8gbm90CiAgICAgICAgICAgICAgICAgICAgICAgIC8vbWVzcyB1cCB0aGUgaXRlcmF0aW9uLgogICAgICAgICAgICAgICAgICAgICAgICBlYWNoUmV2ZXJzZShkZWZRdWV1ZSwgZnVuY3Rpb24oYXJncywgaSkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGFyZ3NbMF0gPT09IGlkKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVmUXVldWUuc3BsaWNlKGksIDEpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9KTsKICAgICAgICAgICAgICAgICAgICAgICAgZGVsZXRlIGNvbnRleHQuZGVmUXVldWVNYXBbaWRdOwoKICAgICAgICAgICAgICAgICAgICAgICAgaWYgKG1vZCkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgLy9Ib2xkIG9uIHRvIGxpc3RlbmVycyBpbiBjYXNlIHRoZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgLy9tb2R1bGUgd2lsbCBiZSBhdHRlbXB0ZWQgdG8gYmUgcmVsb2FkZWQKICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vdXNpbmcgYSBkaWZmZXJlbnQgY29uZmlnLgogICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKG1vZC5ldmVudHMuZGVmaW5lZCkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVuZGVmRXZlbnRzW2lkXSA9IG1vZC5ldmVudHM7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgICAgICAgICAgICAgY2xlYW5SZWdpc3RyeShpZCk7CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICB9OwogICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgIHJldHVybiBsb2NhbFJlcXVpcmU7CiAgICAgICAgICAgIH0sCgogICAgICAgICAgICAvKioKICAgICAgICAgICAgICogQ2FsbGVkIHRvIGVuYWJsZSBhIG1vZHVsZSBpZiBpdCBpcyBzdGlsbCBpbiB0aGUgcmVnaXN0cnkKICAgICAgICAgICAgICogYXdhaXRpbmcgZW5hYmxlbWVudC4gQSBzZWNvbmQgYXJnLCBwYXJlbnQsIHRoZSBwYXJlbnQgbW9kdWxlLAogICAgICAgICAgICAgKiBpcyBwYXNzZWQgaW4gZm9yIGNvbnRleHQsIHdoZW4gdGhpcyBtZXRob2QgaXMgb3ZlcnJpZGRlbiBieQogICAgICAgICAgICAgKiB0aGUgb3B0aW1pemVyLiBOb3Qgc2hvd24gaGVyZSB0byBrZWVwIGNvZGUgY29tcGFjdC4KICAgICAgICAgICAgICovCiAgICAgICAgICAgIGVuYWJsZTogZnVuY3Rpb24gKGRlcE1hcCkgewogICAgICAgICAgICAgICAgdmFyIG1vZCA9IGdldE93bihyZWdpc3RyeSwgZGVwTWFwLmlkKTsKICAgICAgICAgICAgICAgIGlmIChtb2QpIHsKICAgICAgICAgICAgICAgICAgICBnZXRNb2R1bGUoZGVwTWFwKS5lbmFibGUoKTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKCiAgICAgICAgICAgIC8qKgogICAgICAgICAgICAgKiBJbnRlcm5hbCBtZXRob2QgdXNlZCBieSBlbnZpcm9ubWVudCBhZGFwdGVycyB0byBjb21wbGV0ZSBhIGxvYWQgZXZlbnQuCiAgICAgICAgICAgICAqIEEgbG9hZCBldmVudCBjb3VsZCBiZSBhIHNjcmlwdCBsb2FkIG9yIGp1c3QgYSBsb2FkIHBhc3MgZnJvbSBhIHN5bmNocm9ub3VzCiAgICAgICAgICAgICAqIGxvYWQgY2FsbC4KICAgICAgICAgICAgICogQHBhcmFtIHtTdHJpbmd9IG1vZHVsZU5hbWUgdGhlIG5hbWUgb2YgdGhlIG1vZHVsZSB0byBwb3RlbnRpYWxseSBjb21wbGV0ZS4KICAgICAgICAgICAgICovCiAgICAgICAgICAgIGNvbXBsZXRlTG9hZDogZnVuY3Rpb24gKG1vZHVsZU5hbWUpIHsKICAgICAgICAgICAgICAgIHZhciBmb3VuZCwgYXJncywgbW9kLAogICAgICAgICAgICAgICAgICAgIHNoaW0gPSBnZXRPd24oY29uZmlnLnNoaW0sIG1vZHVsZU5hbWUpIHx8IHt9LAogICAgICAgICAgICAgICAgICAgIHNoRXhwb3J0cyA9IHNoaW0uZXhwb3J0czsKCiAgICAgICAgICAgICAgICB0YWtlR2xvYmFsUXVldWUoKTsKCiAgICAgICAgICAgICAgICB3aGlsZSAoZGVmUXVldWUubGVuZ3RoKSB7CiAgICAgICAgICAgICAgICAgICAgYXJncyA9IGRlZlF1ZXVlLnNoaWZ0KCk7CiAgICAgICAgICAgICAgICAgICAgaWYgKGFyZ3NbMF0gPT09IG51bGwpIHsKICAgICAgICAgICAgICAgICAgICAgICAgYXJnc1swXSA9IG1vZHVsZU5hbWU7CiAgICAgICAgICAgICAgICAgICAgICAgIC8vSWYgYWxyZWFkeSBmb3VuZCBhbiBhbm9ueW1vdXMgbW9kdWxlIGFuZCBib3VuZCBpdAogICAgICAgICAgICAgICAgICAgICAgICAvL3RvIHRoaXMgbmFtZSwgdGhlbiB0aGlzIGlzIHNvbWUgb3RoZXIgYW5vbiBtb2R1bGUKICAgICAgICAgICAgICAgICAgICAgICAgLy93YWl0aW5nIGZvciBpdHMgY29tcGxldGVMb2FkIHRvIGZpcmUuCiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChmb3VuZCkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgZm91bmQgPSB0cnVlOwogICAgICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAoYXJnc1swXSA9PT0gbW9kdWxlTmFtZSkgewogICAgICAgICAgICAgICAgICAgICAgICAvL0ZvdW5kIG1hdGNoaW5nIGRlZmluZSBjYWxsIGZvciB0aGlzIHNjcmlwdCEKICAgICAgICAgICAgICAgICAgICAgICAgZm91bmQgPSB0cnVlOwogICAgICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICAgICAgY2FsbEdldE1vZHVsZShhcmdzKTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIGNvbnRleHQuZGVmUXVldWVNYXAgPSB7fTsKCiAgICAgICAgICAgICAgICAvL0RvIHRoaXMgYWZ0ZXIgdGhlIGN5Y2xlIG9mIGNhbGxHZXRNb2R1bGUgaW4gY2FzZSB0aGUgcmVzdWx0CiAgICAgICAgICAgICAgICAvL29mIHRob3NlIGNhbGxzL2luaXQgY2FsbHMgY2hhbmdlcyB0aGUgcmVnaXN0cnkuCiAgICAgICAgICAgICAgICBtb2QgPSBnZXRPd24ocmVnaXN0cnksIG1vZHVsZU5hbWUpOwoKICAgICAgICAgICAgICAgIGlmICghZm91bmQgJiYgIWhhc1Byb3AoZGVmaW5lZCwgbW9kdWxlTmFtZSkgJiYgbW9kICYmICFtb2QuaW5pdGVkKSB7CiAgICAgICAgICAgICAgICAgICAgaWYgKGNvbmZpZy5lbmZvcmNlRGVmaW5lICYmICghc2hFeHBvcnRzIHx8ICFnZXRHbG9iYWwoc2hFeHBvcnRzKSkpIHsKICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGhhc1BhdGhGYWxsYmFjayhtb2R1bGVOYW1lKSkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuOwogICAgICAgICAgICAgICAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIG9uRXJyb3IobWFrZUVycm9yKCdub2RlZmluZScsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdObyBkZWZpbmUgY2FsbCBmb3IgJyArIG1vZHVsZU5hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG51bGwsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFttb2R1bGVOYW1lXSkpOwogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgICAgICAgICAgICAgLy9BIHNjcmlwdCB0aGF0IGRvZXMgbm90IGNhbGwgZGVmaW5lKCksIHNvIGp1c3Qgc2ltdWxhdGUKICAgICAgICAgICAgICAgICAgICAgICAgLy90aGUgY2FsbCBmb3IgaXQuCiAgICAgICAgICAgICAgICAgICAgICAgIGNhbGxHZXRNb2R1bGUoW21vZHVsZU5hbWUsIChzaGltLmRlcHMgfHwgW10pLCBzaGltLmV4cG9ydHNGbl0pOwogICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICBjaGVja0xvYWRlZCgpOwogICAgICAgICAgICB9LAoKICAgICAgICAgICAgLyoqCiAgICAgICAgICAgICAqIENvbnZlcnRzIGEgbW9kdWxlIG5hbWUgdG8gYSBmaWxlIHBhdGguIFN1cHBvcnRzIGNhc2VzIHdoZXJlCiAgICAgICAgICAgICAqIG1vZHVsZU5hbWUgbWF5IGFjdHVhbGx5IGJlIGp1c3QgYW4gVVJMLgogICAgICAgICAgICAgKiBOb3RlIHRoYXQgaXQgKipkb2VzIG5vdCoqIGNhbGwgbm9ybWFsaXplIG9uIHRoZSBtb2R1bGVOYW1lLAogICAgICAgICAgICAgKiBpdCBpcyBhc3N1bWVkIHRvIGhhdmUgYWxyZWFkeSBiZWVuIG5vcm1hbGl6ZWQuIFRoaXMgaXMgYW4KICAgICAgICAgICAgICogaW50ZXJuYWwgQVBJLCBub3QgYSBwdWJsaWMgb25lLiBVc2UgdG9VcmwgZm9yIHRoZSBwdWJsaWMgQVBJLgogICAgICAgICAgICAgKi8KICAgICAgICAgICAgbmFtZVRvVXJsOiBmdW5jdGlvbiAobW9kdWxlTmFtZSwgZXh0LCBza2lwRXh0KSB7CiAgICAgICAgICAgICAgICB2YXIgcGF0aHMsIHN5bXMsIGksIHBhcmVudE1vZHVsZSwgdXJsLAogICAgICAgICAgICAgICAgICAgIHBhcmVudFBhdGgsIGJ1bmRsZUlkLAogICAgICAgICAgICAgICAgICAgIHBrZ01haW4gPSBnZXRPd24oY29uZmlnLnBrZ3MsIG1vZHVsZU5hbWUpOwoKICAgICAgICAgICAgICAgIGlmIChwa2dNYWluKSB7CiAgICAgICAgICAgICAgICAgICAgbW9kdWxlTmFtZSA9IHBrZ01haW47CiAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgYnVuZGxlSWQgPSBnZXRPd24oYnVuZGxlc01hcCwgbW9kdWxlTmFtZSk7CgogICAgICAgICAgICAgICAgaWYgKGJ1bmRsZUlkKSB7CiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGNvbnRleHQubmFtZVRvVXJsKGJ1bmRsZUlkLCBleHQsIHNraXBFeHQpOwogICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgIC8vSWYgYSBjb2xvbiBpcyBpbiB0aGUgVVJMLCBpdCBpbmRpY2F0ZXMgYSBwcm90b2NvbCBpcyB1c2VkIGFuZCBpdCBpcyBqdXN0CiAgICAgICAgICAgICAgICAvL2FuIFVSTCB0byBhIGZpbGUsIG9yIGlmIGl0IHN0YXJ0cyB3aXRoIGEgc2xhc2gsIGNvbnRhaW5zIGEgcXVlcnkgYXJnIChpLmUuID8pCiAgICAgICAgICAgICAgICAvL29yIGVuZHMgd2l0aCAuanMsIHRoZW4gYXNzdW1lIHRoZSB1c2VyIG1lYW50IHRvIHVzZSBhbiB1cmwgYW5kIG5vdCBhIG1vZHVsZSBpZC4KICAgICAgICAgICAgICAgIC8vVGhlIHNsYXNoIGlzIGltcG9ydGFudCBmb3IgcHJvdG9jb2wtbGVzcyBVUkxzIGFzIHdlbGwgYXMgZnVsbCBwYXRocy4KICAgICAgICAgICAgICAgIGlmIChyZXEuanNFeHRSZWdFeHAudGVzdChtb2R1bGVOYW1lKSkgewogICAgICAgICAgICAgICAgICAgIC8vSnVzdCBhIHBsYWluIHBhdGgsIG5vdCBtb2R1bGUgbmFtZSBsb29rdXAsIHNvIGp1c3QgcmV0dXJuIGl0LgogICAgICAgICAgICAgICAgICAgIC8vQWRkIGV4dGVuc2lvbiBpZiBpdCBpcyBpbmNsdWRlZC4gVGhpcyBpcyBhIGJpdCB3b25reSwgb25seSBub24tLmpzIHRoaW5ncyBwYXNzCiAgICAgICAgICAgICAgICAgICAgLy9hbiBleHRlbnNpb24sIHRoaXMgbWV0aG9kIHByb2JhYmx5IG5lZWRzIHRvIGJlIHJld29ya2VkLgogICAgICAgICAgICAgICAgICAgIHVybCA9IG1vZHVsZU5hbWUgKyAoZXh0IHx8ICcnKTsKICAgICAgICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgICAgICAgICAgLy9BIG1vZHVsZSB0aGF0IG5lZWRzIHRvIGJlIGNvbnZlcnRlZCB0byBhIHBhdGguCiAgICAgICAgICAgICAgICAgICAgcGF0aHMgPSBjb25maWcucGF0aHM7CgogICAgICAgICAgICAgICAgICAgIHN5bXMgPSBtb2R1bGVOYW1lLnNwbGl0KCcvJyk7CiAgICAgICAgICAgICAgICAgICAgLy9Gb3IgZWFjaCBtb2R1bGUgbmFtZSBzZWdtZW50LCBzZWUgaWYgdGhlcmUgaXMgYSBwYXRoCiAgICAgICAgICAgICAgICAgICAgLy9yZWdpc3RlcmVkIGZvciBpdC4gU3RhcnQgd2l0aCBtb3N0IHNwZWNpZmljIG5hbWUKICAgICAgICAgICAgICAgICAgICAvL2FuZCB3b3JrIHVwIGZyb20gaXQuCiAgICAgICAgICAgICAgICAgICAgZm9yIChpID0gc3ltcy5sZW5ndGg7IGkgPiAwOyBpIC09IDEpIHsKICAgICAgICAgICAgICAgICAgICAgICAgcGFyZW50TW9kdWxlID0gc3ltcy5zbGljZSgwLCBpKS5qb2luKCcvJyk7CgogICAgICAgICAgICAgICAgICAgICAgICBwYXJlbnRQYXRoID0gZ2V0T3duKHBhdGhzLCBwYXJlbnRNb2R1bGUpOwogICAgICAgICAgICAgICAgICAgICAgICBpZiAocGFyZW50UGF0aCkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgLy9JZiBhbiBhcnJheSwgaXQgbWVhbnMgdGhlcmUgYXJlIGEgZmV3IGNob2ljZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvL0Nob29zZSB0aGUgb25lIHRoYXQgaXMgZGVzaXJlZAogICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGlzQXJyYXkocGFyZW50UGF0aCkpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXJlbnRQYXRoID0gcGFyZW50UGF0aFswXTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN5bXMuc3BsaWNlKDAsIGksIHBhcmVudFBhdGgpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgICAgIC8vSm9pbiB0aGUgcGF0aCBwYXJ0cyB0b2dldGhlciwgdGhlbiBmaWd1cmUgb3V0IGlmIGJhc2VVcmwgaXMgbmVlZGVkLgogICAgICAgICAgICAgICAgICAgIHVybCA9IHN5bXMuam9pbignLycpOwogICAgICAgICAgICAgICAgICAgIHVybCArPSAoZXh0IHx8ICgvXmRhdGFcOnxcPy8udGVzdCh1cmwpIHx8IHNraXBFeHQgPyAnJyA6ICcuanMnKSk7CiAgICAgICAgICAgICAgICAgICAgdXJsID0gKHVybC5jaGFyQXQoMCkgPT09ICcvJyB8fCB1cmwubWF0Y2goL15bXHdcK1wuXC1dKzovKSA/ICcnIDogY29uZmlnLmJhc2VVcmwpICsgdXJsOwogICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgIHJldHVybiBjb25maWcudXJsQXJncyA/IHVybCArCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoKHVybC5pbmRleE9mKCc/JykgPT09IC0xID8gJz8nIDogJyYnKSArCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29uZmlnLnVybEFyZ3MpIDogdXJsOwogICAgICAgICAgICB9LAoKICAgICAgICAgICAgLy9EZWxlZ2F0ZXMgdG8gcmVxLmxvYWQuIEJyb2tlbiBvdXQgYXMgYSBzZXBhcmF0ZSBmdW5jdGlvbiB0bwogICAgICAgICAgICAvL2FsbG93IG92ZXJyaWRpbmcgaW4gdGhlIG9wdGltaXplci4KICAgICAgICAgICAgbG9hZDogZnVuY3Rpb24gKGlkLCB1cmwpIHsKICAgICAgICAgICAgICAgIHJlcS5sb2FkKGNvbnRleHQsIGlkLCB1cmwpOwogICAgICAgICAgICB9LAoKICAgICAgICAgICAgLyoqCiAgICAgICAgICAgICAqIEV4ZWN1dGVzIGEgbW9kdWxlIGNhbGxiYWNrIGZ1bmN0aW9uLiBCcm9rZW4gb3V0IGFzIGEgc2VwYXJhdGUgZnVuY3Rpb24KICAgICAgICAgICAgICogc29sZWx5IHRvIGFsbG93IHRoZSBidWlsZCBzeXN0ZW0gdG8gc2VxdWVuY2UgdGhlIGZpbGVzIGluIHRoZSBidWlsdAogICAgICAgICAgICAgKiBsYXllciBpbiB0aGUgcmlnaHQgc2VxdWVuY2UuCiAgICAgICAgICAgICAqCiAgICAgICAgICAgICAqIEBwcml2YXRlCiAgICAgICAgICAgICAqLwogICAgICAgICAgICBleGVjQ2I6IGZ1bmN0aW9uIChuYW1lLCBjYWxsYmFjaywgYXJncywgZXhwb3J0cykgewogICAgICAgICAgICAgICAgcmV0dXJuIGNhbGxiYWNrLmFwcGx5KGV4cG9ydHMsIGFyZ3MpOwogICAgICAgICAgICB9LAoKICAgICAgICAgICAgLyoqCiAgICAgICAgICAgICAqIGNhbGxiYWNrIGZvciBzY3JpcHQgbG9hZHMsIHVzZWQgdG8gY2hlY2sgc3RhdHVzIG9mIGxvYWRpbmcuCiAgICAgICAgICAgICAqCiAgICAgICAgICAgICAqIEBwYXJhbSB7RXZlbnR9IGV2dCB0aGUgZXZlbnQgZnJvbSB0aGUgYnJvd3NlciBmb3IgdGhlIHNjcmlwdAogICAgICAgICAgICAgKiB0aGF0IHdhcyBsb2FkZWQuCiAgICAgICAgICAgICAqLwogICAgICAgICAgICBvblNjcmlwdExvYWQ6IGZ1bmN0aW9uIChldnQpIHsKICAgICAgICAgICAgICAgIC8vVXNpbmcgY3VycmVudFRhcmdldCBpbnN0ZWFkIG9mIHRhcmdldCBmb3IgRmlyZWZveCAyLjAncyBzYWtlLiBOb3QKICAgICAgICAgICAgICAgIC8vYWxsIG9sZCBicm93c2VycyB3aWxsIGJlIHN1cHBvcnRlZCwgYnV0IHRoaXMgb25lIHdhcyBlYXN5IGVub3VnaAogICAgICAgICAgICAgICAgLy90byBzdXBwb3J0IGFuZCBzdGlsbCBtYWtlcyBzZW5zZS4KICAgICAgICAgICAgICAgIGlmIChldnQudHlwZSA9PT0gJ2xvYWQnIHx8CiAgICAgICAgICAgICAgICAgICAgICAgIChyZWFkeVJlZ0V4cC50ZXN0KChldnQuY3VycmVudFRhcmdldCB8fCBldnQuc3JjRWxlbWVudCkucmVhZHlTdGF0ZSkpKSB7CiAgICAgICAgICAgICAgICAgICAgLy9SZXNldCBpbnRlcmFjdGl2ZSBzY3JpcHQgc28gYSBzY3JpcHQgbm9kZSBpcyBub3QgaGVsZCBvbnRvIGZvcgogICAgICAgICAgICAgICAgICAgIC8vdG8gbG9uZy4KICAgICAgICAgICAgICAgICAgICBpbnRlcmFjdGl2ZVNjcmlwdCA9IG51bGw7CgogICAgICAgICAgICAgICAgICAgIC8vUHVsbCBvdXQgdGhlIG5hbWUgb2YgdGhlIG1vZHVsZSBhbmQgdGhlIGNvbnRleHQuCiAgICAgICAgICAgICAgICAgICAgdmFyIGRhdGEgPSBnZXRTY3JpcHREYXRhKGV2dCk7CiAgICAgICAgICAgICAgICAgICAgY29udGV4dC5jb21wbGV0ZUxvYWQoZGF0YS5pZCk7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCgogICAgICAgICAgICAvKioKICAgICAgICAgICAgICogQ2FsbGJhY2sgZm9yIHNjcmlwdCBlcnJvcnMuCiAgICAgICAgICAgICAqLwogICAgICAgICAgICBvblNjcmlwdEVycm9yOiBmdW5jdGlvbiAoZXZ0KSB7CiAgICAgICAgICAgICAgICB2YXIgZGF0YSA9IGdldFNjcmlwdERhdGEoZXZ0KTsKICAgICAgICAgICAgICAgIGlmICghaGFzUGF0aEZhbGxiYWNrKGRhdGEuaWQpKSB7CiAgICAgICAgICAgICAgICAgICAgdmFyIHBhcmVudHMgPSBbXTsKICAgICAgICAgICAgICAgICAgICBlYWNoUHJvcChyZWdpc3RyeSwgZnVuY3Rpb24odmFsdWUsIGtleSkgewogICAgICAgICAgICAgICAgICAgICAgICBpZiAoa2V5LmluZGV4T2YoJ19AcicpICE9PSAwKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBlYWNoKHZhbHVlLmRlcE1hcHMsIGZ1bmN0aW9uKGRlcE1hcCkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIChkZXBNYXAuaWQgPT09IGRhdGEuaWQpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFyZW50cy5wdXNoKGtleSk7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiB0cnVlOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgfSk7CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICB9KTsKICAgICAgICAgICAgICAgICAgICByZXR1cm4gb25FcnJvcihtYWtlRXJyb3IoJ3NjcmlwdGVycm9yJywgJ1NjcmlwdCBlcnJvciBmb3IgIicgKyBkYXRhLmlkICsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKHBhcmVudHMubGVuZ3RoID8KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJyIsIG5lZWRlZCBieTogJyArIHBhcmVudHMuam9pbignLCAnKSA6CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICciJyksIGV2dCwgW2RhdGEuaWRdKSk7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICB9OwoKICAgICAgICBjb250ZXh0LnJlcXVpcmUgPSBjb250ZXh0Lm1ha2VSZXF1aXJlKCk7CiAgICAgICAgcmV0dXJuIGNvbnRleHQ7CiAgICB9CgogICAgLyoqCiAgICAgKiBNYWluIGVudHJ5IHBvaW50LgogICAgICoKICAgICAqIElmIHRoZSBvbmx5IGFyZ3VtZW50IHRvIHJlcXVpcmUgaXMgYSBzdHJpbmcsIHRoZW4gdGhlIG1vZHVsZSB0aGF0CiAgICAgKiBpcyByZXByZXNlbnRlZCBieSB0aGF0IHN0cmluZyBpcyBmZXRjaGVkIGZvciB0aGUgYXBwcm9wcmlhdGUgY29udGV4dC4KICAgICAqCiAgICAgKiBJZiB0aGUgZmlyc3QgYXJndW1lbnQgaXMgYW4gYXJyYXksIHRoZW4gaXQgd2lsbCBiZSB0cmVhdGVkIGFzIGFuIGFycmF5CiAgICAgKiBvZiBkZXBlbmRlbmN5IHN0cmluZyBuYW1lcyB0byBmZXRjaC4gQW4gb3B0aW9uYWwgZnVuY3Rpb24gY2FsbGJhY2sgY2FuCiAgICAgKiBiZSBzcGVjaWZpZWQgdG8gZXhlY3V0ZSB3aGVuIGFsbCBvZiB0aG9zZSBkZXBlbmRlbmNpZXMgYXJlIGF2YWlsYWJsZS4KICAgICAqCiAgICAgKiBNYWtlIGEgbG9jYWwgcmVxIHZhcmlhYmxlIHRvIGhlbHAgQ2FqYSBjb21wbGlhbmNlIChpdCBhc3N1bWVzIHRoaW5ncwogICAgICogb24gYSByZXF1aXJlIHRoYXQgYXJlIG5vdCBzdGFuZGFyZGl6ZWQpLCBhbmQgdG8gZ2l2ZSBhIHNob3J0CiAgICAgKiBuYW1lIGZvciBtaW5pZmljYXRpb24vbG9jYWwgc2NvcGUgdXNlLgogICAgICovCiAgICByZXEgPSByZXF1aXJlanMgPSBmdW5jdGlvbiAoZGVwcywgY2FsbGJhY2ssIGVycmJhY2ssIG9wdGlvbmFsKSB7CgogICAgICAgIC8vRmluZCB0aGUgcmlnaHQgY29udGV4dCwgdXNlIGRlZmF1bHQKICAgICAgICB2YXIgY29udGV4dCwgY29uZmlnLAogICAgICAgICAgICBjb250ZXh0TmFtZSA9IGRlZkNvbnRleHROYW1lOwoKICAgICAgICAvLyBEZXRlcm1pbmUgaWYgaGF2ZSBjb25maWcgb2JqZWN0IGluIHRoZSBjYWxsLgogICAgICAgIGlmICghaXNBcnJheShkZXBzKSAmJiB0eXBlb2YgZGVwcyAhPT0gJ3N0cmluZycpIHsKICAgICAgICAgICAgLy8gZGVwcyBpcyBhIGNvbmZpZyBvYmplY3QKICAgICAgICAgICAgY29uZmlnID0gZGVwczsKICAgICAgICAgICAgaWYgKGlzQXJyYXkoY2FsbGJhY2spKSB7CiAgICAgICAgICAgICAgICAvLyBBZGp1c3QgYXJncyBpZiB0aGVyZSBhcmUgZGVwZW5kZW5jaWVzCiAgICAgICAgICAgICAgICBkZXBzID0gY2FsbGJhY2s7CiAgICAgICAgICAgICAgICBjYWxsYmFjayA9IGVycmJhY2s7CiAgICAgICAgICAgICAgICBlcnJiYWNrID0gb3B0aW9uYWw7CiAgICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgICAgICBkZXBzID0gW107CiAgICAgICAgICAgIH0KICAgICAgICB9CgogICAgICAgIGlmIChjb25maWcgJiYgY29uZmlnLmNvbnRleHQpIHsKICAgICAgICAgICAgY29udGV4dE5hbWUgPSBjb25maWcuY29udGV4dDsKICAgICAgICB9CgogICAgICAgIGNvbnRleHQgPSBnZXRPd24oY29udGV4dHMsIGNvbnRleHROYW1lKTsKICAgICAgICBpZiAoIWNvbnRleHQpIHsKICAgICAgICAgICAgY29udGV4dCA9IGNvbnRleHRzW2NvbnRleHROYW1lXSA9IHJlcS5zLm5ld0NvbnRleHQoY29udGV4dE5hbWUpOwogICAgICAgIH0KCiAgICAgICAgaWYgKGNvbmZpZykgewogICAgICAgICAgICBjb250ZXh0LmNvbmZpZ3VyZShjb25maWcpOwogICAgICAgIH0KCiAgICAgICAgcmV0dXJuIGNvbnRleHQucmVxdWlyZShkZXBzLCBjYWxsYmFjaywgZXJyYmFjayk7CiAgICB9OwoKICAgIC8qKgogICAgICogU3VwcG9ydCByZXF1aXJlLmNvbmZpZygpIHRvIG1ha2UgaXQgZWFzaWVyIHRvIGNvb3BlcmF0ZSB3aXRoIG90aGVyCiAgICAgKiBBTUQgbG9hZGVycyBvbiBnbG9iYWxseSBhZ3JlZWQgbmFtZXMuCiAgICAgKi8KICAgIHJlcS5jb25maWcgPSBmdW5jdGlvbiAoY29uZmlnKSB7CiAgICAgICAgcmV0dXJuIHJlcShjb25maWcpOwogICAgfTsKCiAgICAvKioKICAgICAqIEV4ZWN1dGUgc29tZXRoaW5nIGFmdGVyIHRoZSBjdXJyZW50IHRpY2sKICAgICAqIG9mIHRoZSBldmVudCBsb29wLiBPdmVycmlkZSBmb3Igb3RoZXIgZW52cwogICAgICogdGhhdCBoYXZlIGEgYmV0dGVyIHNvbHV0aW9uIHRoYW4gc2V0VGltZW91dC4KICAgICAqIEBwYXJhbSAge0Z1bmN0aW9ufSBmbiBmdW5jdGlvbiB0byBleGVjdXRlIGxhdGVyLgogICAgICovCiAgICByZXEubmV4dFRpY2sgPSB0eXBlb2Ygc2V0VGltZW91dCAhPT0gJ3VuZGVmaW5lZCcgPyBmdW5jdGlvbiAoZm4pIHsKICAgICAgICBzZXRUaW1lb3V0KGZuLCA0KTsKICAgIH0gOiBmdW5jdGlvbiAoZm4pIHsgZm4oKTsgfTsKCiAgICAvKioKICAgICAqIEV4cG9ydCByZXF1aXJlIGFzIGEgZ2xvYmFsLCBidXQgb25seSBpZiBpdCBkb2VzIG5vdCBhbHJlYWR5IGV4aXN0LgogICAgICovCiAgICBpZiAoIXJlcXVpcmUpIHsKICAgICAgICByZXF1aXJlID0gcmVxOwogICAgfQoKICAgIHJlcS52ZXJzaW9uID0gdmVyc2lvbjsKCiAgICAvL1VzZWQgdG8gZmlsdGVyIG91dCBkZXBlbmRlbmNpZXMgdGhhdCBhcmUgYWxyZWFkeSBwYXRocy4KICAgIHJlcS5qc0V4dFJlZ0V4cCA9IC9eXC98OnxcP3xcLmpzJC87CiAgICByZXEuaXNCcm93c2VyID0gaXNCcm93c2VyOwogICAgcyA9IHJlcS5zID0gewogICAgICAgIGNvbnRleHRzOiBjb250ZXh0cywKICAgICAgICBuZXdDb250ZXh0OiBuZXdDb250ZXh0CiAgICB9OwoKICAgIC8vQ3JlYXRlIGRlZmF1bHQgY29udGV4dC4KICAgIHJlcSh7fSk7CgogICAgLy9FeHBvcnRzIHNvbWUgY29udGV4dC1zZW5zaXRpdmUgbWV0aG9kcyBvbiBnbG9iYWwgcmVxdWlyZS4KICAgIGVhY2goWwogICAgICAgICd0b1VybCcsCiAgICAgICAgJ3VuZGVmJywKICAgICAgICAnZGVmaW5lZCcsCiAgICAgICAgJ3NwZWNpZmllZCcKICAgIF0sIGZ1bmN0aW9uIChwcm9wKSB7CiAgICAgICAgLy9SZWZlcmVuY2UgZnJvbSBjb250ZXh0cyBpbnN0ZWFkIG9mIGVhcmx5IGJpbmRpbmcgdG8gZGVmYXVsdCBjb250ZXh0LAogICAgICAgIC8vc28gdGhhdCBkdXJpbmcgYnVpbGRzLCB0aGUgbGF0ZXN0IGluc3RhbmNlIG9mIHRoZSBkZWZhdWx0IGNvbnRleHQKICAgICAgICAvL3dpdGggaXRzIGNvbmZpZyBnZXRzIHVzZWQuCiAgICAgICAgcmVxW3Byb3BdID0gZnVuY3Rpb24gKCkgewogICAgICAgICAgICB2YXIgY3R4ID0gY29udGV4dHNbZGVmQ29udGV4dE5hbWVdOwogICAgICAgICAgICByZXR1cm4gY3R4LnJlcXVpcmVbcHJvcF0uYXBwbHkoY3R4LCBhcmd1bWVudHMpOwogICAgICAgIH07CiAgICB9KTsKCiAgICBpZiAoaXNCcm93c2VyKSB7CiAgICAgICAgaGVhZCA9IHMuaGVhZCA9IGRvY3VtZW50LmdldEVsZW1lbnRzQnlUYWdOYW1lKCdoZWFkJylbMF07CiAgICAgICAgLy9JZiBCQVNFIHRhZyBpcyBpbiBwbGF5LCB1c2luZyBhcHBlbmRDaGlsZCBpcyBhIHByb2JsZW0gZm9yIElFNi4KICAgICAgICAvL1doZW4gdGhhdCBicm93c2VyIGRpZXMsIHRoaXMgY2FuIGJlIHJlbW92ZWQuIERldGFpbHMgaW4gdGhpcyBqUXVlcnkgYnVnOgogICAgICAgIC8vaHR0cDovL2Rldi5qcXVlcnkuY29tL3RpY2tldC8yNzA5CiAgICAgICAgYmFzZUVsZW1lbnQgPSBkb2N1bWVudC5nZXRFbGVtZW50c0J5VGFnTmFtZSgnYmFzZScpWzBdOwogICAgICAgIGlmIChiYXNlRWxlbWVudCkgewogICAgICAgICAgICBoZWFkID0gcy5oZWFkID0gYmFzZUVsZW1lbnQucGFyZW50Tm9kZTsKICAgICAgICB9CiAgICB9CgogICAgLyoqCiAgICAgKiBBbnkgZXJyb3JzIHRoYXQgcmVxdWlyZSBleHBsaWNpdGx5IGdlbmVyYXRlcyB3aWxsIGJlIHBhc3NlZCB0byB0aGlzCiAgICAgKiBmdW5jdGlvbi4gSW50ZXJjZXB0L292ZXJyaWRlIGl0IGlmIHlvdSB3YW50IGN1c3RvbSBlcnJvciBoYW5kbGluZy4KICAgICAqIEBwYXJhbSB7RXJyb3J9IGVyciB0aGUgZXJyb3Igb2JqZWN0LgogICAgICovCiAgICByZXEub25FcnJvciA9IGRlZmF1bHRPbkVycm9yOwoKICAgIC8qKgogICAgICogQ3JlYXRlcyB0aGUgbm9kZSBmb3IgdGhlIGxvYWQgY29tbWFuZC4gT25seSB1c2VkIGluIGJyb3dzZXIgZW52cy4KICAgICAqLwogICAgcmVxLmNyZWF0ZU5vZGUgPSBmdW5jdGlvbiAoY29uZmlnLCBtb2R1bGVOYW1lLCB1cmwpIHsKICAgICAgICB2YXIgbm9kZSA9IGNvbmZpZy54aHRtbCA/CiAgICAgICAgICAgICAgICBkb2N1bWVudC5jcmVhdGVFbGVtZW50TlMoJ2h0dHA6Ly93d3cudzMub3JnLzE5OTkveGh0bWwnLCAnaHRtbDpzY3JpcHQnKSA6CiAgICAgICAgICAgICAgICBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzY3JpcHQnKTsKICAgICAgICBub2RlLnR5cGUgPSBjb25maWcuc2NyaXB0VHlwZSB8fCAndGV4dC9qYXZhc2NyaXB0JzsKICAgICAgICBub2RlLmNoYXJzZXQgPSAndXRmLTgnOwogICAgICAgIG5vZGUuYXN5bmMgPSB0cnVlOwogICAgICAgIHJldHVybiBub2RlOwogICAgfTsKCiAgICAvKioKICAgICAqIERvZXMgdGhlIHJlcXVlc3QgdG8gbG9hZCBhIG1vZHVsZSBmb3IgdGhlIGJyb3dzZXIgY2FzZS4KICAgICAqIE1ha2UgdGhpcyBhIHNlcGFyYXRlIGZ1bmN0aW9uIHRvIGFsbG93IG90aGVyIGVudmlyb25tZW50cwogICAgICogdG8gb3ZlcnJpZGUgaXQuCiAgICAgKgogICAgICogQHBhcmFtIHtPYmplY3R9IGNvbnRleHQgdGhlIHJlcXVpcmUgY29udGV4dCB0byBmaW5kIHN0YXRlLgogICAgICogQHBhcmFtIHtTdHJpbmd9IG1vZHVsZU5hbWUgdGhlIG5hbWUgb2YgdGhlIG1vZHVsZS4KICAgICAqIEBwYXJhbSB7T2JqZWN0fSB1cmwgdGhlIFVSTCB0byB0aGUgbW9kdWxlLgogICAgICovCiAgICByZXEubG9hZCA9IGZ1bmN0aW9uIChjb250ZXh0LCBtb2R1bGVOYW1lLCB1cmwpIHsKICAgICAgICB2YXIgY29uZmlnID0gKGNvbnRleHQgJiYgY29udGV4dC5jb25maWcpIHx8IHt9LAogICAgICAgICAgICBub2RlOwogICAgICAgIGlmIChpc0Jyb3dzZXIpIHsKICAgICAgICAgICAgLy9JbiB0aGUgYnJvd3NlciBzbyB1c2UgYSBzY3JpcHQgdGFnCiAgICAgICAgICAgIG5vZGUgPSByZXEuY3JlYXRlTm9kZShjb25maWcsIG1vZHVsZU5hbWUsIHVybCk7CiAgICAgICAgICAgIGlmIChjb25maWcub25Ob2RlQ3JlYXRlZCkgewogICAgICAgICAgICAgICAgY29uZmlnLm9uTm9kZUNyZWF0ZWQobm9kZSwgY29uZmlnLCBtb2R1bGVOYW1lLCB1cmwpOwogICAgICAgICAgICB9CgogICAgICAgICAgICBub2RlLnNldEF0dHJpYnV0ZSgnZGF0YS1yZXF1aXJlY29udGV4dCcsIGNvbnRleHQuY29udGV4dE5hbWUpOwogICAgICAgICAgICBub2RlLnNldEF0dHJpYnV0ZSgnZGF0YS1yZXF1aXJlbW9kdWxlJywgbW9kdWxlTmFtZSk7CgogICAgICAgICAgICAvL1NldCB1cCBsb2FkIGxpc3RlbmVyLiBUZXN0IGF0dGFjaEV2ZW50IGZpcnN0IGJlY2F1c2UgSUU5IGhhcwogICAgICAgICAgICAvL2Egc3VidGxlIGlzc3VlIGluIGl0cyBhZGRFdmVudExpc3RlbmVyIGFuZCBzY3JpcHQgb25sb2FkIGZpcmluZ3MKICAgICAgICAgICAgLy90aGF0IGRvIG5vdCBtYXRjaCB0aGUgYmVoYXZpb3Igb2YgYWxsIG90aGVyIGJyb3dzZXJzIHdpdGgKICAgICAgICAgICAgLy9hZGRFdmVudExpc3RlbmVyIHN1cHBvcnQsIHdoaWNoIGZpcmUgdGhlIG9ubG9hZCBldmVudCBmb3IgYQogICAgICAgICAgICAvL3NjcmlwdCByaWdodCBhZnRlciB0aGUgc2NyaXB0IGV4ZWN1dGlvbi4gU2VlOgogICAgICAgICAgICAvL2h0dHBzOi8vY29ubmVjdC5taWNyb3NvZnQuY29tL0lFL2ZlZWRiYWNrL2RldGFpbHMvNjQ4MDU3L3NjcmlwdC1vbmxvYWQtZXZlbnQtaXMtbm90LWZpcmVkLWltbWVkaWF0ZWx5LWFmdGVyLXNjcmlwdC1leGVjdXRpb24KICAgICAgICAgICAgLy9VTkZPUlRVTkFURUxZIE9wZXJhIGltcGxlbWVudHMgYXR0YWNoRXZlbnQgYnV0IGRvZXMgbm90IGZvbGxvdyB0aGUgc2NyaXB0CiAgICAgICAgICAgIC8vc2NyaXB0IGV4ZWN1dGlvbiBtb2RlLgogICAgICAgICAgICBpZiAobm9kZS5hdHRhY2hFdmVudCAmJgogICAgICAgICAgICAgICAgICAgIC8vQ2hlY2sgaWYgbm9kZS5hdHRhY2hFdmVudCBpcyBhcnRpZmljaWFsbHkgYWRkZWQgYnkgY3VzdG9tIHNjcmlwdCBvcgogICAgICAgICAgICAgICAgICAgIC8vbmF0aXZlbHkgc3VwcG9ydGVkIGJ5IGJyb3dzZXIKICAgICAgICAgICAgICAgICAgICAvL3JlYWQgaHR0cHM6Ly9naXRodWIuY29tL2pyYnVya2UvcmVxdWlyZWpzL2lzc3Vlcy8xODcKICAgICAgICAgICAgICAgICAgICAvL2lmIHdlIGNhbiBOT1QgZmluZCBbbmF0aXZlIGNvZGVdIHRoZW4gaXQgbXVzdCBOT1QgbmF0aXZlbHkgc3VwcG9ydGVkLgogICAgICAgICAgICAgICAgICAgIC8vaW4gSUU4LCBub2RlLmF0dGFjaEV2ZW50IGRvZXMgbm90IGhhdmUgdG9TdHJpbmcoKQogICAgICAgICAgICAgICAgICAgIC8vTm90ZSB0aGUgdGVzdCBmb3IgIltuYXRpdmUgY29kZSIgd2l0aCBubyBjbG9zaW5nIGJyYWNlLCBzZWU6CiAgICAgICAgICAgICAgICAgICAgLy9odHRwczovL2dpdGh1Yi5jb20vanJidXJrZS9yZXF1aXJlanMvaXNzdWVzLzI3MwogICAgICAgICAgICAgICAgICAgICEobm9kZS5hdHRhY2hFdmVudC50b1N0cmluZyAmJiBub2RlLmF0dGFjaEV2ZW50LnRvU3RyaW5nKCkuaW5kZXhPZignW25hdGl2ZSBjb2RlJykgPCAwKSAmJgogICAgICAgICAgICAgICAgICAgICFpc09wZXJhKSB7CiAgICAgICAgICAgICAgICAvL1Byb2JhYmx5IElFLiBJRSAoYXQgbGVhc3QgNi04KSBkbyBub3QgZmlyZQogICAgICAgICAgICAgICAgLy9zY3JpcHQgb25sb2FkIHJpZ2h0IGFmdGVyIGV4ZWN1dGluZyB0aGUgc2NyaXB0LCBzbwogICAgICAgICAgICAgICAgLy93ZSBjYW5ub3QgdGllIHRoZSBhbm9ueW1vdXMgZGVmaW5lIGNhbGwgdG8gYSBuYW1lLgogICAgICAgICAgICAgICAgLy9Ib3dldmVyLCBJRSByZXBvcnRzIHRoZSBzY3JpcHQgYXMgYmVpbmcgaW4gJ2ludGVyYWN0aXZlJwogICAgICAgICAgICAgICAgLy9yZWFkeVN0YXRlIGF0IHRoZSB0aW1lIG9mIHRoZSBkZWZpbmUgY2FsbC4KICAgICAgICAgICAgICAgIHVzZUludGVyYWN0aXZlID0gdHJ1ZTsKCiAgICAgICAgICAgICAgICBub2RlLmF0dGFjaEV2ZW50KCdvbnJlYWR5c3RhdGVjaGFuZ2UnLCBjb250ZXh0Lm9uU2NyaXB0TG9hZCk7CiAgICAgICAgICAgICAgICAvL0l0IHdvdWxkIGJlIGdyZWF0IHRvIGFkZCBhbiBlcnJvciBoYW5kbGVyIGhlcmUgdG8gY2F0Y2gKICAgICAgICAgICAgICAgIC8vNDA0cyBpbiBJRTkrLiBIb3dldmVyLCBvbnJlYWR5c3RhdGVjaGFuZ2Ugd2lsbCBmaXJlIGJlZm9yZQogICAgICAgICAgICAgICAgLy90aGUgZXJyb3IgaGFuZGxlciwgc28gdGhhdCBkb2VzIG5vdCBoZWxwLiBJZiBhZGRFdmVudExpc3RlbmVyCiAgICAgICAgICAgICAgICAvL2lzIHVzZWQsIHRoZW4gSUUgd2lsbCBmaXJlIGVycm9yIGJlZm9yZSBsb2FkLCBidXQgd2UgY2Fubm90CiAgICAgICAgICAgICAgICAvL3VzZSB0aGF0IHBhdGh3YXkgZ2l2ZW4gdGhlIGNvbm5lY3QubWljcm9zb2Z0LmNvbSBpc3N1ZQogICAgICAgICAgICAgICAgLy9tZW50aW9uZWQgYWJvdmUgYWJvdXQgbm90IGRvaW5nIHRoZSAnc2NyaXB0IGV4ZWN1dGUsCiAgICAgICAgICAgICAgICAvL3RoZW4gZmlyZSB0aGUgc2NyaXB0IGxvYWQgZXZlbnQgbGlzdGVuZXIgYmVmb3JlIGV4ZWN1dGUKICAgICAgICAgICAgICAgIC8vbmV4dCBzY3JpcHQnIHRoYXQgb3RoZXIgYnJvd3NlcnMgZG8uCiAgICAgICAgICAgICAgICAvL0Jlc3QgaG9wZTogSUUxMCBmaXhlcyB0aGUgaXNzdWVzLAogICAgICAgICAgICAgICAgLy9hbmQgdGhlbiBkZXN0cm95cyBhbGwgaW5zdGFsbHMgb2YgSUUgNi05LgogICAgICAgICAgICAgICAgLy9ub2RlLmF0dGFjaEV2ZW50KCdvbmVycm9yJywgY29udGV4dC5vblNjcmlwdEVycm9yKTsKICAgICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgICAgIG5vZGUuYWRkRXZlbnRMaXN0ZW5lcignbG9hZCcsIGNvbnRleHQub25TY3JpcHRMb2FkLCBmYWxzZSk7CiAgICAgICAgICAgICAgICBub2RlLmFkZEV2ZW50TGlzdGVuZXIoJ2Vycm9yJywgY29udGV4dC5vblNjcmlwdEVycm9yLCBmYWxzZSk7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgbm9kZS5zcmMgPSB1cmw7CgogICAgICAgICAgICAvL0ZvciBzb21lIGNhY2hlIGNhc2VzIGluIElFIDYtOCwgdGhlIHNjcmlwdCBleGVjdXRlcyBiZWZvcmUgdGhlIGVuZAogICAgICAgICAgICAvL29mIHRoZSBhcHBlbmRDaGlsZCBleGVjdXRpb24sIHNvIHRvIHRpZSBhbiBhbm9ueW1vdXMgZGVmaW5lCiAgICAgICAgICAgIC8vY2FsbCB0byB0aGUgbW9kdWxlIG5hbWUgKHdoaWNoIGlzIHN0b3JlZCBvbiB0aGUgbm9kZSksIGhvbGQgb24KICAgICAgICAgICAgLy90byBhIHJlZmVyZW5jZSB0byB0aGlzIG5vZGUsIGJ1dCBjbGVhciBhZnRlciB0aGUgRE9NIGluc2VydGlvbi4KICAgICAgICAgICAgY3VycmVudGx5QWRkaW5nU2NyaXB0ID0gbm9kZTsKICAgICAgICAgICAgaWYgKGJhc2VFbGVtZW50KSB7CiAgICAgICAgICAgICAgICBoZWFkLmluc2VydEJlZm9yZShub2RlLCBiYXNlRWxlbWVudCk7CiAgICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgICAgICBoZWFkLmFwcGVuZENoaWxkKG5vZGUpOwogICAgICAgICAgICB9CiAgICAgICAgICAgIGN1cnJlbnRseUFkZGluZ1NjcmlwdCA9IG51bGw7CgogICAgICAgICAgICByZXR1cm4gbm9kZTsKICAgICAgICB9IGVsc2UgaWYgKGlzV2ViV29ya2VyKSB7CiAgICAgICAgICAgIHRyeSB7CiAgICAgICAgICAgICAgICAvL0luIGEgd2ViIHdvcmtlciwgdXNlIGltcG9ydFNjcmlwdHMuIFRoaXMgaXMgbm90IGEgdmVyeQogICAgICAgICAgICAgICAgLy9lZmZpY2llbnQgdXNlIG9mIGltcG9ydFNjcmlwdHMsIGltcG9ydFNjcmlwdHMgd2lsbCBibG9jayB1bnRpbAogICAgICAgICAgICAgICAgLy9pdHMgc2NyaXB0IGlzIGRvd25sb2FkZWQgYW5kIGV2YWx1YXRlZC4gSG93ZXZlciwgaWYgd2ViIHdvcmtlcnMKICAgICAgICAgICAgICAgIC8vYXJlIGluIHBsYXksIHRoZSBleHBlY3RhdGlvbiBpcyB0aGF0IGEgYnVpbGQgaGFzIGJlZW4gZG9uZSBzbwogICAgICAgICAgICAgICAgLy90aGF0IG9ubHkgb25lIHNjcmlwdCBuZWVkcyB0byBiZSBsb2FkZWQgYW55d2F5LiBUaGlzIG1heSBuZWVkCiAgICAgICAgICAgICAgICAvL3RvIGJlIHJlZXZhbHVhdGVkIGlmIG90aGVyIHVzZSBjYXNlcyBiZWNvbWUgY29tbW9uLgogICAgICAgICAgICAgICAgaW1wb3J0U2NyaXB0cyh1cmwpOwoKICAgICAgICAgICAgICAgIC8vQWNjb3VudCBmb3IgYW5vbnltb3VzIG1vZHVsZXMKICAgICAgICAgICAgICAgIGNvbnRleHQuY29tcGxldGVMb2FkKG1vZHVsZU5hbWUpOwogICAgICAgICAgICB9IGNhdGNoIChlKSB7CiAgICAgICAgICAgICAgICBjb250ZXh0Lm9uRXJyb3IobWFrZUVycm9yKCdpbXBvcnRzY3JpcHRzJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnaW1wb3J0U2NyaXB0cyBmYWlsZWQgZm9yICcgKwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtb2R1bGVOYW1lICsgJyBhdCAnICsgdXJsLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgW21vZHVsZU5hbWVdKSk7CiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICB9OwoKICAgIGZ1bmN0aW9uIGdldEludGVyYWN0aXZlU2NyaXB0KCkgewogICAgICAgIGlmIChpbnRlcmFjdGl2ZVNjcmlwdCAmJiBpbnRlcmFjdGl2ZVNjcmlwdC5yZWFkeVN0YXRlID09PSAnaW50ZXJhY3RpdmUnKSB7CiAgICAgICAgICAgIHJldHVybiBpbnRlcmFjdGl2ZVNjcmlwdDsKICAgICAgICB9CgogICAgICAgIGVhY2hSZXZlcnNlKHNjcmlwdHMoKSwgZnVuY3Rpb24gKHNjcmlwdCkgewogICAgICAgICAgICBpZiAoc2NyaXB0LnJlYWR5U3RhdGUgPT09ICdpbnRlcmFjdGl2ZScpIHsKICAgICAgICAgICAgICAgIHJldHVybiAoaW50ZXJhY3RpdmVTY3JpcHQgPSBzY3JpcHQpOwogICAgICAgICAgICB9CiAgICAgICAgfSk7CiAgICAgICAgcmV0dXJuIGludGVyYWN0aXZlU2NyaXB0OwogICAgfQoKICAgIC8vTG9vayBmb3IgYSBkYXRhLW1haW4gc2NyaXB0IGF0dHJpYnV0ZSwgd2hpY2ggY291bGQgYWxzbyBhZGp1c3QgdGhlIGJhc2VVcmwuCiAgICBpZiAoaXNCcm93c2VyICYmICFjZmcuc2tpcERhdGFNYWluKSB7CiAgICAgICAgLy9GaWd1cmUgb3V0IGJhc2VVcmwuIEdldCBpdCBmcm9tIHRoZSBzY3JpcHQgdGFnIHdpdGggcmVxdWlyZS5qcyBpbiBpdC4KICAgICAgICBlYWNoUmV2ZXJzZShzY3JpcHRzKCksIGZ1bmN0aW9uIChzY3JpcHQpIHsKICAgICAgICAgICAgLy9TZXQgdGhlICdoZWFkJyB3aGVyZSB3ZSBjYW4gYXBwZW5kIGNoaWxkcmVuIGJ5CiAgICAgICAgICAgIC8vdXNpbmcgdGhlIHNjcmlwdCdzIHBhcmVudC4KICAgICAgICAgICAgaWYgKCFoZWFkKSB7CiAgICAgICAgICAgICAgICBoZWFkID0gc2NyaXB0LnBhcmVudE5vZGU7CiAgICAgICAgICAgIH0KCiAgICAgICAgICAgIC8vTG9vayBmb3IgYSBkYXRhLW1haW4gYXR0cmlidXRlIHRvIHNldCBtYWluIHNjcmlwdCBmb3IgdGhlIHBhZ2UKICAgICAgICAgICAgLy90byBsb2FkLiBJZiBpdCBpcyB0aGVyZSwgdGhlIHBhdGggdG8gZGF0YSBtYWluIGJlY29tZXMgdGhlCiAgICAgICAgICAgIC8vYmFzZVVybCwgaWYgaXQgaXMgbm90IGFscmVhZHkgc2V0LgogICAgICAgICAgICBkYXRhTWFpbiA9IHNjcmlwdC5nZXRBdHRyaWJ1dGUoJ2RhdGEtbWFpbicpOwogICAgICAgICAgICBpZiAoZGF0YU1haW4pIHsKICAgICAgICAgICAgICAgIC8vUHJlc2VydmUgZGF0YU1haW4gaW4gY2FzZSBpdCBpcyBhIHBhdGggKGkuZS4gY29udGFpbnMgJz8nKQogICAgICAgICAgICAgICAgbWFpblNjcmlwdCA9IGRhdGFNYWluOwoKICAgICAgICAgICAgICAgIC8vU2V0IGZpbmFsIGJhc2VVcmwgaWYgdGhlcmUgaXMgbm90IGFscmVhZHkgYW4gZXhwbGljaXQgb25lLgogICAgICAgICAgICAgICAgaWYgKCFjZmcuYmFzZVVybCkgewogICAgICAgICAgICAgICAgICAgIC8vUHVsbCBvZmYgdGhlIGRpcmVjdG9yeSBvZiBkYXRhLW1haW4gZm9yIHVzZSBhcyB0aGUKICAgICAgICAgICAgICAgICAgICAvL2Jhc2VVcmwuCiAgICAgICAgICAgICAgICAgICAgc3JjID0gbWFpblNjcmlwdC5zcGxpdCgnLycpOwogICAgICAgICAgICAgICAgICAgIG1haW5TY3JpcHQgPSBzcmMucG9wKCk7CiAgICAgICAgICAgICAgICAgICAgc3ViUGF0aCA9IHNyYy5sZW5ndGggPyBzcmMuam9pbignLycpICArICcvJyA6ICcuLyc7CgogICAgICAgICAgICAgICAgICAgIGNmZy5iYXNlVXJsID0gc3ViUGF0aDsKICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICAvL1N0cmlwIG9mZiBhbnkgdHJhaWxpbmcgLmpzIHNpbmNlIG1haW5TY3JpcHQgaXMgbm93CiAgICAgICAgICAgICAgICAvL2xpa2UgYSBtb2R1bGUgbmFtZS4KICAgICAgICAgICAgICAgIG1haW5TY3JpcHQgPSBtYWluU2NyaXB0LnJlcGxhY2UoanNTdWZmaXhSZWdFeHAsICcnKTsKCiAgICAgICAgICAgICAgICAvL0lmIG1haW5TY3JpcHQgaXMgc3RpbGwgYSBwYXRoLCBmYWxsIGJhY2sgdG8gZGF0YU1haW4KICAgICAgICAgICAgICAgIGlmIChyZXEuanNFeHRSZWdFeHAudGVzdChtYWluU2NyaXB0KSkgewogICAgICAgICAgICAgICAgICAgIG1haW5TY3JpcHQgPSBkYXRhTWFpbjsKICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICAvL1B1dCB0aGUgZGF0YS1tYWluIHNjcmlwdCBpbiB0aGUgZmlsZXMgdG8gbG9hZC4KICAgICAgICAgICAgICAgIGNmZy5kZXBzID0gY2ZnLmRlcHMgPyBjZmcuZGVwcy5jb25jYXQobWFpblNjcmlwdCkgOiBbbWFpblNjcmlwdF07CgogICAgICAgICAgICAgICAgcmV0dXJuIHRydWU7CiAgICAgICAgICAgIH0KICAgICAgICB9KTsKICAgIH0KCiAgICAvKioKICAgICAqIFRoZSBmdW5jdGlvbiB0aGF0IGhhbmRsZXMgZGVmaW5pdGlvbnMgb2YgbW9kdWxlcy4gRGlmZmVycyBmcm9tCiAgICAgKiByZXF1aXJlKCkgaW4gdGhhdCBhIHN0cmluZyBmb3IgdGhlIG1vZHVsZSBzaG91bGQgYmUgdGhlIGZpcnN0IGFyZ3VtZW50LAogICAgICogYW5kIHRoZSBmdW5jdGlvbiB0byBleGVjdXRlIGFmdGVyIGRlcGVuZGVuY2llcyBhcmUgbG9hZGVkIHNob3VsZAogICAgICogcmV0dXJuIGEgdmFsdWUgdG8gZGVmaW5lIHRoZSBtb2R1bGUgY29ycmVzcG9uZGluZyB0byB0aGUgZmlyc3QgYXJndW1lbnQncwogICAgICogbmFtZS4KICAgICAqLwogICAgZGVmaW5lID0gZnVuY3Rpb24gKG5hbWUsIGRlcHMsIGNhbGxiYWNrKSB7CiAgICAgICAgdmFyIG5vZGUsIGNvbnRleHQ7CgogICAgICAgIC8vQWxsb3cgZm9yIGFub255bW91cyBtb2R1bGVzCiAgICAgICAgaWYgKHR5cGVvZiBuYW1lICE9PSAnc3RyaW5nJykgewogICAgICAgICAgICAvL0FkanVzdCBhcmdzIGFwcHJvcHJpYXRlbHkKICAgICAgICAgICAgY2FsbGJhY2sgPSBkZXBzOwogICAgICAgICAgICBkZXBzID0gbmFtZTsKICAgICAgICAgICAgbmFtZSA9IG51bGw7CiAgICAgICAgfQoKICAgICAgICAvL1RoaXMgbW9kdWxlIG1heSBub3QgaGF2ZSBkZXBlbmRlbmNpZXMKICAgICAgICBpZiAoIWlzQXJyYXkoZGVwcykpIHsKICAgICAgICAgICAgY2FsbGJhY2sgPSBkZXBzOwogICAgICAgICAgICBkZXBzID0gbnVsbDsKICAgICAgICB9CgogICAgICAgIC8vSWYgbm8gbmFtZSwgYW5kIGNhbGxiYWNrIGlzIGEgZnVuY3Rpb24sIHRoZW4gZmlndXJlIG91dCBpZiBpdCBhCiAgICAgICAgLy9Db21tb25KUyB0aGluZyB3aXRoIGRlcGVuZGVuY2llcy4KICAgICAgICBpZiAoIWRlcHMgJiYgaXNGdW5jdGlvbihjYWxsYmFjaykpIHsKICAgICAgICAgICAgZGVwcyA9IFtdOwogICAgICAgICAgICAvL1JlbW92ZSBjb21tZW50cyBmcm9tIHRoZSBjYWxsYmFjayBzdHJpbmcsCiAgICAgICAgICAgIC8vbG9vayBmb3IgcmVxdWlyZSBjYWxscywgYW5kIHB1bGwgdGhlbSBpbnRvIHRoZSBkZXBlbmRlbmNpZXMsCiAgICAgICAgICAgIC8vYnV0IG9ubHkgaWYgdGhlcmUgYXJlIGZ1bmN0aW9uIGFyZ3MuCiAgICAgICAgICAgIGlmIChjYWxsYmFjay5sZW5ndGgpIHsKICAgICAgICAgICAgICAgIGNhbGxiYWNrCiAgICAgICAgICAgICAgICAgICAgLnRvU3RyaW5nKCkKICAgICAgICAgICAgICAgICAgICAucmVwbGFjZShjb21tZW50UmVnRXhwLCAnJykKICAgICAgICAgICAgICAgICAgICAucmVwbGFjZShjanNSZXF1aXJlUmVnRXhwLCBmdW5jdGlvbiAobWF0Y2gsIGRlcCkgewogICAgICAgICAgICAgICAgICAgICAgICBkZXBzLnB1c2goZGVwKTsKICAgICAgICAgICAgICAgICAgICB9KTsKCiAgICAgICAgICAgICAgICAvL01heSBiZSBhIENvbW1vbkpTIHRoaW5nIGV2ZW4gd2l0aG91dCByZXF1aXJlIGNhbGxzLCBidXQgc3RpbGwKICAgICAgICAgICAgICAgIC8vY291bGQgdXNlIGV4cG9ydHMsIGFuZCBtb2R1bGUuIEF2b2lkIGRvaW5nIGV4cG9ydHMgYW5kIG1vZHVsZQogICAgICAgICAgICAgICAgLy93b3JrIHRob3VnaCBpZiBpdCBqdXN0IG5lZWRzIHJlcXVpcmUuCiAgICAgICAgICAgICAgICAvL1JFUVVJUkVTIHRoZSBmdW5jdGlvbiB0byBleHBlY3QgdGhlIENvbW1vbkpTIHZhcmlhYmxlcyBpbiB0aGUKICAgICAgICAgICAgICAgIC8vb3JkZXIgbGlzdGVkIGJlbG93LgogICAgICAgICAgICAgICAgZGVwcyA9IChjYWxsYmFjay5sZW5ndGggPT09IDEgPyBbJ3JlcXVpcmUnXSA6IFsncmVxdWlyZScsICdleHBvcnRzJywgJ21vZHVsZSddKS5jb25jYXQoZGVwcyk7CiAgICAgICAgICAgIH0KICAgICAgICB9CgogICAgICAgIC8vSWYgaW4gSUUgNi04IGFuZCBoaXQgYW4gYW5vbnltb3VzIGRlZmluZSgpIGNhbGwsIGRvIHRoZSBpbnRlcmFjdGl2ZQogICAgICAgIC8vd29yay4KICAgICAgICBpZiAodXNlSW50ZXJhY3RpdmUpIHsKICAgICAgICAgICAgbm9kZSA9IGN1cnJlbnRseUFkZGluZ1NjcmlwdCB8fCBnZXRJbnRlcmFjdGl2ZVNjcmlwdCgpOwogICAgICAgICAgICBpZiAobm9kZSkgewogICAgICAgICAgICAgICAgaWYgKCFuYW1lKSB7CiAgICAgICAgICAgICAgICAgICAgbmFtZSA9IG5vZGUuZ2V0QXR0cmlidXRlKCdkYXRhLXJlcXVpcmVtb2R1bGUnKTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIGNvbnRleHQgPSBjb250ZXh0c1tub2RlLmdldEF0dHJpYnV0ZSgnZGF0YS1yZXF1aXJlY29udGV4dCcpXTsKICAgICAgICAgICAgfQogICAgICAgIH0KCiAgICAgICAgLy9BbHdheXMgc2F2ZSBvZmYgZXZhbHVhdGluZyB0aGUgZGVmIGNhbGwgdW50aWwgdGhlIHNjcmlwdCBvbmxvYWQgaGFuZGxlci4KICAgICAgICAvL1RoaXMgYWxsb3dzIG11bHRpcGxlIG1vZHVsZXMgdG8gYmUgaW4gYSBmaWxlIHdpdGhvdXQgcHJlbWF0dXJlbHkKICAgICAgICAvL3RyYWNpbmcgZGVwZW5kZW5jaWVzLCBhbmQgYWxsb3dzIGZvciBhbm9ueW1vdXMgbW9kdWxlIHN1cHBvcnQsCiAgICAgICAgLy93aGVyZSB0aGUgbW9kdWxlIG5hbWUgaXMgbm90IGtub3duIHVudGlsIHRoZSBzY3JpcHQgb25sb2FkIGV2ZW50CiAgICAgICAgLy9vY2N1cnMuIElmIG5vIGNvbnRleHQsIHVzZSB0aGUgZ2xvYmFsIHF1ZXVlLCBhbmQgZ2V0IGl0IHByb2Nlc3NlZAogICAgICAgIC8vaW4gdGhlIG9uc2NyaXB0IGxvYWQgY2FsbGJhY2suCiAgICAgICAgaWYgKGNvbnRleHQpIHsKICAgICAgICAgICAgY29udGV4dC5kZWZRdWV1ZS5wdXNoKFtuYW1lLCBkZXBzLCBjYWxsYmFja10pOwogICAgICAgICAgICBjb250ZXh0LmRlZlF1ZXVlTWFwW25hbWVdID0gdHJ1ZTsKICAgICAgICB9IGVsc2UgewogICAgICAgICAgICBnbG9iYWxEZWZRdWV1ZS5wdXNoKFtuYW1lLCBkZXBzLCBjYWxsYmFja10pOwogICAgICAgIH0KICAgIH07CgogICAgZGVmaW5lLmFtZCA9IHsKICAgICAgICBqUXVlcnk6IHRydWUKICAgIH07CgogICAgLyoqCiAgICAgKiBFeGVjdXRlcyB0aGUgdGV4dC4gTm9ybWFsbHkganVzdCB1c2VzIGV2YWwsIGJ1dCBjYW4gYmUgbW9kaWZpZWQKICAgICAqIHRvIHVzZSBhIGJldHRlciwgZW52aXJvbm1lbnQtc3BlY2lmaWMgY2FsbC4gT25seSB1c2VkIGZvciB0cmFuc3BpbGluZwogICAgICogbG9hZGVyIHBsdWdpbnMsIG5vdCBmb3IgcGxhaW4gSlMgbW9kdWxlcy4KICAgICAqIEBwYXJhbSB7U3RyaW5nfSB0ZXh0IHRoZSB0ZXh0IHRvIGV4ZWN1dGUvZXZhbHVhdGUuCiAgICAgKi8KICAgIHJlcS5leGVjID0gZnVuY3Rpb24gKHRleHQpIHsKICAgICAgICAvKmpzbGludCBldmlsOiB0cnVlICovCiAgICAgICAgcmV0dXJuIGV2YWwodGV4dCk7CiAgICB9OwoKICAgIC8vU2V0IHVwIHdpdGggY29uZmlnIGluZm8uCiAgICByZXEoY2ZnKTsKfSh0aGlzKSk7Cg==",
"ok": true,
"headers": [
[
"content-type",
"application/javascript"
]
],
"status": 200,
"status_text": ""
}
},
"base_uri": "https://localhost:8080/",
"height": 1000
}
},
"source": [
"# pure LASSO (not LASSO followed by OLS on selected subset) (Shouldnt make a difference)\n",
"configure_plotly_browser_state()\n",
"def create_model_lasso():\n",
" return LassoLarsIC(criterion='aic')\n",
"\n",
"# use all predictors at each timestep\n",
"backtestmodel = BacktestModel(X, Y,\n",
" create_model=create_model_lasso,\n",
" coef_dict_param=\"all\",\n",
" startindex=FIRST_TRAIN_MONTHS,\n",
" fit_missing='mean',\n",
" scaler=None)\n",
"backtestmodel.gen_predictions(verbose=False)\n",
"backtestmodel.gen_returns(calc_returns, verbose=False)\n",
"backtestmodel.report_returns(start_date=start_date_str, freq='M')\n",
"backtestmodel.evaluate_predictions()\n",
"backtestmodel.evaluate_quantiles(chart=True, verbose=False)\n",
"perf_LASSO_only = backtestmodel.cumulative_return\n",
"mychart([perf_LASSO_only],[\"LASSO only\"], title=\"LASSO only\")\n"
],
"execution_count": null,
"outputs": [
{
"output_type": "display_data",
"data": {
"text/html": [
"\n",
" <script src=\"/static/components/requirejs/require.js\"></script>\n",
" <script>\n",
" requirejs.config({\n",
" paths: {\n",
" base: '/static/base',\n",
" plotly: 'https://cdn.plot.ly/plotly-latest.min.js?noext',\n",
" },\n",
" });\n",
" </script>\n",
" "
],
"text/plain": [
"<IPython.core.display.HTML object>"
]
},
"metadata": {}
},
{
"output_type": "stream",
"name": "stdout",
"text": [
"................................................................................\n",
"15:10:33 Still training step 80 of 563\n",
"................................................................................\n",
"15:11:08 Still training step 160 of 563\n",
"................................................................................\n",
"15:11:55 Still training step 240 of 563\n",
"................................................................................\n",
"15:12:43 Still training step 320 of 563\n",
"................................................................................\n",
"15:13:32 Still training step 400 of 563\n",
"................................................................................\n",
"15:14:21 Still training step 480 of 563\n",
"................................................................................\n",
"15:15:12 Still training step 560 of 563\n",
"...\n",
"Mean return: 3.109%\n",
"Monthly Sharpe ratio: 0.560\n",
"OOS MSE across all predictions: 40.1228\n",
"In-sample MSE: 35.9913\n",
"Variance: 39.4097\n",
"R-squared: -0.0181\n",
"Avg rank correlation (Kendall's tau): 0.0313 (Expected: 0)\n",
"5-quintile accuracy: 0.2210 (Expected: 0.2)\n",
"Long/short/flat accuracy: 0.4601 (Expected: 0.44)\n",
"Excess true positive in quintiles 1 + 5: 200.800000\n"
]
},
{
"output_type": "display_data",
"data": {
"image/png": "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\n",
"text/plain": [
"<Figure size 720x720 with 2 Axes>"
]
},
"metadata": {
"needs_background": "light"
}
},
{
"output_type": "display_data",
"data": {
"text/html": [
"<div id=\"8b25d9db-ad2e-4245-afd1-1ea23010b7c5\" style=\"height: 600px; width: 900px;\" class=\"plotly-graph-div\"></div><script type=\"text/javascript\">require([\"plotly\"], function(Plotly) { window.PLOTLYENV=window.PLOTLYENV || {};window.PLOTLYENV.BASE_URL=\"https://plot.ly\";Plotly.newPlot(\"8b25d9db-ad2e-4245-afd1-1ea23010b7c5\", [{\"type\": \"scatter\", \"x\": [1970.0, 1970.081850533808, 1970.1637010676156, 1970.2455516014236, 1970.3274021352313, 1970.4092526690392, 1970.491103202847, 1970.5729537366549, 1970.6548042704626, 1970.7366548042705, 1970.8185053380782, 1970.9003558718862, 1970.982206405694, 1971.0640569395018, 1971.1459074733095, 1971.2277580071175, 1971.3096085409252, 1971.3914590747331, 1971.4733096085408, 1971.5551601423488, 1971.6370106761565, 1971.7188612099644, 1971.8007117437724, 1971.88256227758, 1971.964412811388, 1972.0462633451957, 1972.1281138790036, 1972.2099644128114, 1972.2918149466193, 1972.373665480427, 1972.455516014235, 1972.5373665480427, 1972.6192170818506, 1972.7010676156583, 1972.7829181494662, 1972.864768683274, 1972.946619217082, 1973.0284697508896, 1973.1103202846975, 1973.1921708185052, 1973.2740213523132, 1973.355871886121, 1973.4377224199288, 1973.5195729537365, 1973.6014234875445, 1973.6832740213524, 1973.7651245551601, 1973.846975088968, 1973.9288256227758, 1974.0106761565837, 1974.0925266903914, 1974.1743772241994, 1974.256227758007, 1974.338078291815, 1974.4199288256227, 1974.5017793594307, 1974.5836298932384, 1974.6654804270463, 1974.747330960854, 1974.829181494662, 1974.9110320284697, 1974.9928825622776, 1975.0747330960853, 1975.1565836298932, 1975.238434163701, 1975.320284697509, 1975.4021352313168, 1975.4839857651245, 1975.5658362989325, 1975.6476868327402, 1975.7295373665481, 1975.8113879003558, 1975.8932384341638, 1975.9750889679715, 1976.0569395017794, 1976.1387900355871, 1976.220640569395, 1976.3024911032028, 1976.3843416370107, 1976.4661921708184, 1976.5480427046264, 1976.629893238434, 1976.711743772242, 1976.7935943060497, 1976.8754448398577, 1976.9572953736654, 1977.0391459074733, 1977.1209964412812, 1977.202846975089, 1977.284697508897, 1977.3665480427046, 1977.4483985765125, 1977.5302491103203, 1977.6120996441282, 1977.693950177936, 1977.7758007117438, 1977.8576512455516, 1977.9395017793595, 1978.0213523131672, 1978.1032028469751, 1978.1850533807828, 1978.2669039145908, 1978.3487544483985, 1978.4306049822064, 1978.5124555160141, 1978.594306049822, 1978.6761565836298, 1978.7580071174377, 1978.8398576512454, 1978.9217081850534, 1979.0035587188613, 1979.085409252669, 1979.167259786477, 1979.2491103202847, 1979.3309608540926, 1979.4128113879003, 1979.4946619217083, 1979.576512455516, 1979.658362989324, 1979.7402135231316, 1979.8220640569396, 1979.9039145907473, 1979.9857651245552, 1980.067615658363, 1980.1494661921708, 1980.2313167259786, 1980.3131672597865, 1980.3950177935942, 1980.4768683274021, 1980.5587188612099, 1980.6405693950178, 1980.7224199288257, 1980.8042704626334, 1980.8861209964414, 1980.967971530249, 1981.049822064057, 1981.1316725978647, 1981.2135231316727, 1981.2953736654804, 1981.3772241992883, 1981.459074733096, 1981.540925266904, 1981.6227758007117, 1981.7046263345196, 1981.7864768683273, 1981.8683274021353, 1981.950177935943, 1982.032028469751, 1982.1138790035586, 1982.1957295373666, 1982.2775800711743, 1982.3594306049822, 1982.4412811387901, 1982.5231316725979, 1982.6049822064058, 1982.6868327402135, 1982.7686832740214, 1982.8505338078292, 1982.932384341637, 1983.0142348754448, 1983.0960854092527, 1983.1779359430604, 1983.2597864768684, 1983.341637010676, 1983.423487544484, 1983.5053380782917, 1983.5871886120997, 1983.6690391459074, 1983.7508896797153, 1983.832740213523, 1983.914590747331, 1983.9964412811387, 1984.0782918149466, 1984.1601423487546, 1984.2419928825623, 1984.3238434163702, 1984.405693950178, 1984.4875444839859, 1984.5693950177936, 1984.6512455516015, 1984.7330960854092, 1984.8149466192172, 1984.8967971530249, 1984.9786476868328, 1985.0604982206405, 1985.1423487544484, 1985.2241992882562, 1985.306049822064, 1985.3879003558718, 1985.4697508896797, 1985.5516014234875, 1985.6334519572954, 1985.715302491103, 1985.797153024911, 1985.8790035587188, 1985.9608540925267, 1986.0427046263346, 1986.1245551601423, 1986.2064056939503, 1986.288256227758, 1986.370106761566, 1986.4519572953736, 1986.5338078291816, 1986.6156583629893, 1986.6975088967972, 1986.779359430605, 1986.8612099644129, 1986.9430604982206, 1987.0249110320285, 1987.1067615658362, 1987.1886120996442, 1987.2704626334519, 1987.3523131672598, 1987.4341637010675, 1987.5160142348755, 1987.5978647686832, 1987.679715302491, 1987.761565836299, 1987.8434163701068, 1987.9252669039147, 1988.0071174377224, 1988.0889679715303, 1988.170818505338, 1988.252669039146, 1988.3345195729537, 1988.4163701067616, 1988.4982206405693, 1988.5800711743773, 1988.661921708185, 1988.743772241993, 1988.8256227758006, 1988.9074733096086, 1988.9893238434163, 1989.0711743772242, 1989.153024911032, 1989.2348754448399, 1989.3167259786476, 1989.3985765124555, 1989.4804270462632, 1989.5622775800712, 1989.644128113879, 1989.7259786476868, 1989.8078291814948, 1989.8896797153025, 1989.9715302491104, 1990.053380782918, 1990.135231316726, 1990.2170818505338, 1990.2989323843417, 1990.3807829181494, 1990.4626334519573, 1990.544483985765, 1990.626334519573, 1990.7081850533807, 1990.7900355871886, 1990.8718861209964, 1990.9537366548043, 1991.035587188612, 1991.11743772242, 1991.1992882562276, 1991.2811387900356, 1991.3629893238435, 1991.4448398576512, 1991.5266903914592, 1991.6085409252669, 1991.6903914590748, 1991.7722419928825, 1991.8540925266905, 1991.9359430604982, 1992.017793594306, 1992.0996441281138, 1992.1814946619218, 1992.2633451957295, 1992.3451957295374, 1992.4270462633451, 1992.508896797153, 1992.5907473309608, 1992.6725978647687, 1992.7544483985764, 1992.8362989323844, 1992.918149466192, 1993.0, 1993.081850533808, 1993.1637010676156, 1993.2455516014236, 1993.3274021352313, 1993.4092526690392, 1993.491103202847, 1993.5729537366549, 1993.6548042704626, 1993.7366548042705, 1993.8185053380782, 1993.9003558718862, 1993.982206405694, 1994.0640569395018, 1994.1459074733095, 1994.2277580071175, 1994.3096085409252, 1994.3914590747331, 1994.4733096085408, 1994.5551601423488, 1994.6370106761565, 1994.7188612099644, 1994.8007117437724, 1994.88256227758, 1994.964412811388, 1995.0462633451957, 1995.1281138790036, 1995.2099644128114, 1995.2918149466193, 1995.373665480427, 1995.455516014235, 1995.5373665480427, 1995.6192170818506, 1995.7010676156583, 1995.7829181494662, 1995.864768683274, 1995.946619217082, 1996.0284697508896, 1996.1103202846975, 1996.1921708185052, 1996.2740213523132, 1996.355871886121, 1996.4377224199288, 1996.5195729537368, 1996.6014234875445, 1996.6832740213524, 1996.7651245551601, 1996.846975088968, 1996.9288256227758, 1997.0106761565837, 1997.0925266903914, 1997.1743772241994, 1997.256227758007, 1997.338078291815, 1997.4199288256227, 1997.5017793594307, 1997.5836298932384, 1997.6654804270463, 1997.747330960854, 1997.829181494662, 1997.9110320284697, 1997.9928825622776, 1998.0747330960853, 1998.1565836298932, 1998.238434163701, 1998.320284697509, 1998.4021352313168, 1998.4839857651245, 1998.5658362989325, 1998.6476868327402, 1998.7295373665481, 1998.8113879003558, 1998.8932384341638, 1998.9750889679715, 1999.0569395017794, 1999.1387900355871, 1999.220640569395, 1999.3024911032028, 1999.3843416370107, 1999.4661921708184, 1999.5480427046264, 1999.629893238434, 1999.711743772242, 1999.7935943060497, 1999.8754448398577, 1999.9572953736654, 2000.0391459074733, 2000.1209964412812, 2000.202846975089, 2000.284697508897, 2000.3665480427046, 2000.4483985765125, 2000.5302491103203, 2000.6120996441282, 2000.693950177936, 2000.7758007117438, 2000.8576512455516, 2000.9395017793595, 2001.0213523131672, 2001.1032028469751, 2001.1850533807828, 2001.2669039145908, 2001.3487544483985, 2001.4306049822064, 2001.5124555160141, 2001.594306049822, 2001.6761565836298, 2001.7580071174377, 2001.8398576512454, 2001.9217081850534, 2002.0035587188613, 2002.085409252669, 2002.167259786477, 2002.2491103202847, 2002.3309608540926, 2002.4128113879003, 2002.4946619217083, 2002.576512455516, 2002.658362989324, 2002.7402135231316, 2002.8220640569396, 2002.9039145907473, 2002.9857651245552, 2003.067615658363, 2003.1494661921708, 2003.2313167259786, 2003.3131672597865, 2003.3950177935942, 2003.4768683274021, 2003.5587188612099, 2003.6405693950178, 2003.7224199288257, 2003.8042704626334, 2003.8861209964414, 2003.967971530249, 2004.049822064057, 2004.1316725978647, 2004.2135231316727, 2004.2953736654804, 2004.3772241992883, 2004.459074733096, 2004.540925266904, 2004.6227758007117, 2004.7046263345196, 2004.7864768683273, 2004.8683274021353, 2004.950177935943, 2005.032028469751, 2005.1138790035586, 2005.1957295373666, 2005.2775800711743, 2005.3594306049822, 2005.4412811387901, 2005.5231316725979, 2005.6049822064058, 2005.6868327402135, 2005.7686832740214, 2005.8505338078292, 2005.932384341637, 2006.0142348754448, 2006.0960854092527, 2006.1779359430604, 2006.2597864768684, 2006.341637010676, 2006.423487544484, 2006.5053380782917, 2006.5871886120997, 2006.6690391459074, 2006.7508896797153, 2006.832740213523, 2006.914590747331, 2006.9964412811387, 2007.0782918149466, 2007.1601423487546, 2007.2419928825623, 2007.3238434163702, 2007.405693950178, 2007.4875444839859, 2007.5693950177936, 2007.6512455516015, 2007.7330960854092, 2007.8149466192172, 2007.8967971530249, 2007.9786476868328, 2008.0604982206405, 2008.1423487544484, 2008.2241992882562, 2008.306049822064, 2008.3879003558718, 2008.4697508896797, 2008.5516014234875, 2008.6334519572954, 2008.715302491103, 2008.797153024911, 2008.879003558719, 2008.9608540925267, 2009.0427046263346, 2009.1245551601423, 2009.2064056939503, 2009.288256227758, 2009.370106761566, 2009.4519572953736, 2009.5338078291816, 2009.6156583629893, 2009.6975088967972, 2009.779359430605, 2009.8612099644129, 2009.9430604982206, 2010.0249110320285, 2010.1067615658362, 2010.1886120996442, 2010.2704626334519, 2010.3523131672598, 2010.4341637010675, 2010.5160142348755, 2010.5978647686832, 2010.679715302491, 2010.761565836299, 2010.8434163701068, 2010.9252669039147, 2011.0071174377224, 2011.0889679715303, 2011.170818505338, 2011.252669039146, 2011.3345195729537, 2011.4163701067616, 2011.4982206405693, 2011.5800711743773, 2011.661921708185, 2011.743772241993, 2011.8256227758006, 2011.9074733096086, 2011.9893238434163, 2012.0711743772242, 2012.153024911032, 2012.2348754448399, 2012.3167259786476, 2012.3985765124555, 2012.4804270462632, 2012.5622775800712, 2012.644128113879, 2012.7259786476868, 2012.8078291814948, 2012.8896797153025, 2012.9715302491104, 2013.053380782918, 2013.135231316726, 2013.2170818505338, 2013.2989323843417, 2013.3807829181494, 2013.4626334519573, 2013.544483985765, 2013.626334519573, 2013.7081850533807, 2013.7900355871886, 2013.8718861209964, 2013.9537366548043, 2014.035587188612, 2014.11743772242, 2014.1992882562276, 2014.2811387900356, 2014.3629893238435, 2014.4448398576512, 2014.5266903914592, 2014.6085409252669, 2014.6903914590748, 2014.7722419928825, 2014.8540925266905, 2014.9359430604982, 2015.017793594306, 2015.0996441281138, 2015.1814946619218, 2015.2633451957295, 2015.3451957295374, 2015.4270462633451, 2015.508896797153, 2015.5907473309608, 2015.6725978647687, 2015.7544483985764, 2015.8362989323844, 2015.918149466192, 2016.0], \"y\": [100.0, 99.30166666666666, 97.59947059722222, 98.42906609729862, 100.58384240261198, 103.55022755480235, 102.8279647176076, 102.80482842554615, 108.47108788560082, 106.88108252234439, 107.94722132050478, 107.97870592672325, 109.68296983526669, 111.16551797754003, 113.57780971765266, 114.1050000510921, 114.06125980107251, 115.74271287264, 116.246193673636, 114.38528585657721, 114.78849398922164, 115.55662032816618, 115.99958737275749, 115.16922365981415, 118.02542040657752, 119.81252198056711, 119.35324064630825, 119.58896329658472, 120.43405863721395, 119.05809951728376, 120.4530635832946, 122.74669066902648, 121.16734991575167, 121.64394149208695, 120.81879008896563, 121.51047766222496, 123.52147606753479, 128.10721086654203, 129.1598251158288, 129.89173079148517, 131.50780040874923, 135.7500228702681, 129.80643436893155, 129.3131699183296, 129.2776087966021, 128.52133478514196, 133.996343646989, 134.87513633407383, 129.92634279008274, 129.36008047942263, 126.69849682355851, 128.0562823811843, 126.57082950556257, 128.29008327301312, 127.67001453719355, 129.1190692021907, 129.9077715165674, 135.46132874890066, 134.01640790891238, 137.01949224947128, 128.39411521236707, 130.14990473789618, 129.7345096252744, 134.6514475400723, 136.10680526890124, 135.16880253592308, 135.3017185250834, 136.69983628317593, 137.90165567716554, 135.0229586149047, 135.63281231131538, 134.70711836729063, 133.50036709858367, 134.91769599594696, 134.0070015479743, 133.91766354694232, 136.49557857022097, 136.07585466611752, 136.45913499009376, 138.57993737973146, 138.5141119094761, 137.72342718732617, 138.1790621922709, 134.97445944159514, 134.15336481332542, 134.85208025506148, 135.25101765914937, 134.96699052206515, 133.4272421051926, 134.53913578940254, 135.24882973069163, 135.9926982942104, 135.85897214088777, 136.34693228249378, 135.1686675426859, 136.04275825946192, 135.36934660607758, 134.8267411417649, 133.10208241132648, 134.40204608287712, 136.30943512020326, 138.0269340027178, 137.17691813415107, 139.94217617553866, 138.32001311670388, 139.3896878848064, 138.60562089045436, 140.01824317669622, 139.77087761375074, 139.68585032986906, 140.97212420165656, 141.98712349590852, 143.03191207963258, 146.18695767358915, 147.1578827174712, 147.11864061541323, 147.2240756411876, 146.27448035330195, 148.15898324185366, 151.32835082503564, 158.88720194874617, 160.52374012881825, 157.74266633108647, 160.7792126579599, 159.7756824056198, 159.3975466239265, 159.23283582574842, 161.617347542239, 166.3958337845712, 167.10301607815566, 168.86734542291418, 166.25693770825163, 166.62963034361428, 168.80831276035704, 171.6217846396963, 170.6449706487887, 172.39834772220502, 171.24040548667085, 174.0016570251434, 175.08626735393347, 173.4025210828798, 174.12936665041886, 174.4457016665004, 173.25801718098768, 172.15494113826873, 175.3570230434405, 178.60989582089636, 180.41236735288888, 181.52340684850375, 179.76414249713034, 182.64636091516766, 180.95231591767947, 181.96112507892056, 183.57754640670498, 191.06904011298528, 189.29528252393638, 193.92197472095893, 188.21582061479472, 191.93151460676515, 193.34221123912485, 191.87764398898847, 194.6646667679285, 193.71080990076567, 198.48739562156868, 196.65469533532954, 199.68317764349362, 197.2636831410466, 196.95134897607326, 203.16516403626838, 207.30465425350735, 208.29626151635327, 206.3469556689961, 208.42074257346948, 208.9661101832034, 211.44061720462287, 207.78798054241298, 204.5672668440056, 205.082094465563, 206.20320991530807, 207.83908871396952, 207.99496803050502, 205.74862237577554, 212.47660232746338, 212.19684146773227, 213.56197448117467, 215.61750848555596, 209.38616249032341, 209.859026240614, 211.94537472648943, 213.56145820877893, 216.8414062711021, 221.19811152543232, 221.76769666261032, 225.4213194651268, 224.82958850153082, 226.10549641627702, 228.48148834111805, 233.4033604024663, 233.76124555508343, 236.66962505186459, 239.74435793066343, 232.95959260122567, 234.68737624635142, 235.28582905577963, 231.55066651951915, 239.3963749367555, 237.5809524268184, 240.3705487765633, 242.13927539797754, 242.35114726395074, 239.7620291740142, 236.92284714521193, 235.45984856409024, 231.56495023575926, 230.32607775199793, 230.50649984623698, 227.23714932341784, 225.19769590824018, 226.79659954918873, 229.58430775198084, 230.29984551114117, 227.80684968348308, 226.6317460171991, 228.63554837156786, 231.00383159345, 229.2944032396585, 228.2167195444321, 229.19044421448837, 228.51433240405564, 230.89849860547127, 231.35452314021708, 231.50490358025823, 234.26367034792298, 236.99284210747624, 237.59914879520122, 229.83955659413127, 228.77846397452166, 227.5602186538573, 231.66388793024853, 229.65806476725245, 231.71541826412576, 234.07698456860095, 231.59576853217376, 232.97569331967796, 230.01107762218504, 229.88457152949283, 230.44970443450282, 233.20933964510598, 243.55800409185758, 250.99667146682972, 249.44049210373538, 254.4355379581127, 256.61520239995383, 257.64807858961365, 259.62552759278896, 257.9444523016257, 255.78846658780458, 253.8871056528352, 250.47655553356546, 252.67657461300197, 252.807124176552, 251.1807316776829, 255.16194627477415, 258.9383430796408, 264.1106364826567, 263.72547513778613, 264.58917606886234, 261.6323920262928, 264.44275997064193, 264.213576245334, 264.1849531079074, 265.77006282655486, 263.9251756404339, 265.0446582604421, 270.44715187798414, 267.87114275634633, 269.6815052294746, 272.9536408262589, 273.05599844156876, 272.2937171125861, 274.7761281669291, 275.1150187250017, 278.37742432204897, 280.38638140090643, 277.8325287769798, 275.7557306243719, 278.2007647692413, 276.3113179085169, 274.6764759442248, 273.3053825351366, 271.23737180728733, 275.18161525565165, 271.07911600821535, 270.5618066951663, 271.62150710472235, 270.3946832976327, 269.3649302120742, 269.1696406376705, 274.158251310822, 271.0762556356695, 271.794607713104, 275.0901173316254, 276.10565834810797, 273.52176956206694, 274.89849580219607, 278.02088455034936, 275.47467661600905, 273.3695909622017, 275.79346800206656, 281.9574520119128, 282.4461782620668, 278.8120374350948, 279.20237428750397, 281.38247949339893, 282.1820747059593, 284.54534958162174, 289.74304463397937, 291.0903497915274, 292.71560424453, 285.8855734788243, 286.6026697923004, 284.6346647930599, 284.15078586291173, 278.4985531474553, 278.62155667509546, 280.4186657156498, 278.5702393441409, 279.5429137631841, 281.26210268282773, 279.5159337953385, 275.6865655023424, 277.2763580300726, 274.05764164060685, 278.67551290225106, 291.6478580278509, 297.3058264735912, 300.1475746649679, 298.4992642340995, 296.44708179249005, 301.96840869087515, 297.584833958046, 300.57060179209174, 300.93128651424223, 310.34542026069613, 310.59369659690464, 303.1705072482386, 309.14801908281635, 309.67099448176475, 317.7998580869111, 319.2643857662616, 328.23039393319743, 321.3649081934281, 318.07091788444546, 321.34704833865527, 315.0084778101753, 324.750114986455, 324.53361490979734, 337.17960810411574, 340.3800045510373, 337.1747595081817, 352.4094390586264, 356.73232817774556, 345.5636335363807, 349.0595856289904, 367.7139116509797, 363.0408806904152, 358.60573126464726, 361.5941123585193, 356.83312321246547, 362.97660015044005, 365.74127192158585, 358.9445799517097, 350.1414641283941, 342.68928663352807, 340.3847011809176, 342.8638364211853, 337.69802128577277, 343.6189932589833, 345.6549357940428, 353.2247788879323, 359.84774349208107, 358.14146544168943, 349.19986685449527, 351.31543604785537, 353.3823418632702, 364.18111726004196, 368.16586565139556, 357.32338090796196, 356.5938456719415, 365.8266546594642, 362.54640898935105, 366.1930216197689, 375.2288344282367, 375.92613467888253, 370.73522130252496, 363.15368602688835, 358.53255537219616, 365.2998573548464, 361.87517119214465, 356.28419979722605, 348.3153098617614, 354.33535946720554, 346.64628216676715, 348.2524099408065, 342.94446279262536, 334.97100403269684, 337.86571179254605, 328.47586055231153, 325.40734855498533, 327.8180746621968, 324.0672895246035, 319.7950024243708, 314.8968089705709, 322.2863874210803, 323.7554762037414, 324.95606942799697, 319.3451612958736, 316.12509758614016, 312.71621528383633, 309.5968710363801, 312.566421024404, 319.38297372291123, 319.41757354506456, 325.70211430456374, 323.45476971586226, 325.62730758578715, 327.9935326875772, 332.88336960406116, 339.0417119417363, 333.31190700992096, 334.57571465733355, 334.65099419313145, 344.0630534048132, 337.5975351929145, 330.15069622911744, 340.98514157703636, 343.84089213774405, 342.86954161745496, 346.42395586555585, 342.89331838202605, 343.5276710210328, 339.7660430233525, 344.32457076724916, 340.21850026084974, 339.17516352671646, 343.8444749446009, 342.0994642342571, 345.99369646879035, 348.77894572536405, 351.10413869686647, 351.36161506524417, 354.13151579734176, 355.14964390525915, 356.2269311584384, 350.54808016422095, 363.10938637010554, 350.6759157984825, 342.61329203441574, 338.48480186540104, 329.81112881760015, 340.25514789682416, 348.06683900062205, 340.14251729937456, 345.6131427859395, 367.4270923147787, 391.23942978921235, 398.659937640881, 390.9226126845009, 395.6820954939347, 411.6544627487065, 401.7678947350251, 451.49336784006334, 436.4210142436693, 432.8459987686565, 417.42586006252316, 424.8003835902944, 431.38124953274735, 430.1661923465635, 432.13061795827946, 436.55995679235184, 435.5595068913693, 430.5324242493315, 428.22907577959757, 434.9629779962317, 433.8320742534415, 445.3539310921558, 441.5016195882086, 443.4957352366821, 436.0265278957376, 442.908479927692, 446.69534743107374, 449.36807459320295, 452.6147589321388, 452.79580483571164, 447.8112776841452, 442.40022474546174, 456.4427452125906, 455.2978346600157, 464.90841311996417, 475.69041406757134, 505.2268246943836, 480.9085735324272, 470.50491805834235, 474.6022317197671, 466.9018105101138, 466.11196828066755, 472.5715033077571, 469.7124457127452, 477.3452729555774, 473.5623116674044, 473.62150695636285, 469.2247206334513, 471.617766708682, 481.21911840925947, 477.8866760142754, 470.3559784780838, 471.6337788862826, 478.07551024957104, 479.03166127007023, 489.0993100177629, 479.2032006450701, 468.4890157506474, 463.9329600724723, 468.47177086518127, 459.85189028126194, 460.0319989382887, 456.25973654699476, 454.149535265465, 458.55857033700056, 457.8669111600756, 451.0942964324995, 452.9625786435575, 452.40015010840835, 450.7413495580108, 448.6829640616959, 447.4229127376226, 458.27291837151006, 467.2206971027138, 473.92531410613776, 472.3811074576752, 476.61285487865024, 471.8228956871198, 468.6656141434802, 462.03790125013444, 455.25364473344496, 466.3390709827044, 475.68139703805787, 468.538248059203, 458.2030752040971, 467.37859178505914, 459.22283535840995, 452.5679311026743, 463.5427034319142, 453.93578090328776, 454.95713641032023, 464.96998472081737, 467.8527986260865, 458.3397917206894, 441.6600428003206, 443.6548739936354, 440.2202458441347, 438.8005355512874, 420.6195666949458], \"mode\": \"line\", \"name\": \"LASSO only\"}], {\"title\": \"LASSO only\", \"autosize\": false, \"width\": 900, \"height\": 600, \"yaxis\": {\"type\": \"log\", \"autorange\": true}}, {\"showLink\": true, \"linkText\": \"Export to plot.ly\"})});</script>"
],
"text/plain": [
"<IPython.core.display.HTML object>"
]
},
"metadata": {}
}
]
},
{
"cell_type": "code",
"metadata": {
"id": "uPZlQ5vtfkZp",
"outputId": "3c0a33df-073e-4198-87f8-edcee76005e0",
"colab": {
"resources": {
"http://localhost:8080/static/components/requirejs/require.js": {
"data": "LyoqIHZpbTogZXQ6dHM9NDpzdz00OnN0cz00CiAqIEBsaWNlbnNlIFJlcXVpcmVKUyAyLjEuMjIgQ29weXJpZ2h0IChjKSAyMDEwLTIwMTUsIFRoZSBEb2pvIEZvdW5kYXRpb24gQWxsIFJpZ2h0cyBSZXNlcnZlZC4KICogQXZhaWxhYmxlIHZpYSB0aGUgTUlUIG9yIG5ldyBCU0QgbGljZW5zZS4KICogc2VlOiBodHRwOi8vZ2l0aHViLmNvbS9qcmJ1cmtlL3JlcXVpcmVqcyBmb3IgZGV0YWlscwogKi8KLy9Ob3QgdXNpbmcgc3RyaWN0OiB1bmV2ZW4gc3RyaWN0IHN1cHBvcnQgaW4gYnJvd3NlcnMsICMzOTIsIGFuZCBjYXVzZXMKLy9wcm9ibGVtcyB3aXRoIHJlcXVpcmVqcy5leGVjKCkvdHJhbnNwaWxlciBwbHVnaW5zIHRoYXQgbWF5IG5vdCBiZSBzdHJpY3QuCi8qanNsaW50IHJlZ2V4cDogdHJ1ZSwgbm9tZW46IHRydWUsIHNsb3BweTogdHJ1ZSAqLwovKmdsb2JhbCB3aW5kb3csIG5hdmlnYXRvciwgZG9jdW1lbnQsIGltcG9ydFNjcmlwdHMsIHNldFRpbWVvdXQsIG9wZXJhICovCgp2YXIgcmVxdWlyZWpzLCByZXF1aXJlLCBkZWZpbmU7CihmdW5jdGlvbiAoZ2xvYmFsKSB7CiAgICB2YXIgcmVxLCBzLCBoZWFkLCBiYXNlRWxlbWVudCwgZGF0YU1haW4sIHNyYywKICAgICAgICBpbnRlcmFjdGl2ZVNjcmlwdCwgY3VycmVudGx5QWRkaW5nU2NyaXB0LCBtYWluU2NyaXB0LCBzdWJQYXRoLAogICAgICAgIHZlcnNpb24gPSAnMi4xLjIyJywKICAgICAgICBjb21tZW50UmVnRXhwID0gLyhcL1wqKFtcc1xTXSo/KVwqXC98KFteOl18XilcL1wvKC4qKSQpL21nLAogICAgICAgIGNqc1JlcXVpcmVSZWdFeHAgPSAvW14uXVxzKnJlcXVpcmVccypcKFxzKlsiJ10oW14nIlxzXSspWyInXVxzKlwpL2csCiAgICAgICAganNTdWZmaXhSZWdFeHAgPSAvXC5qcyQvLAogICAgICAgIGN1cnJEaXJSZWdFeHAgPSAvXlwuXC8vLAogICAgICAgIG9wID0gT2JqZWN0LnByb3RvdHlwZSwKICAgICAgICBvc3RyaW5nID0gb3AudG9TdHJpbmcsCiAgICAgICAgaGFzT3duID0gb3AuaGFzT3duUHJvcGVydHksCiAgICAgICAgYXAgPSBBcnJheS5wcm90b3R5cGUsCiAgICAgICAgaXNCcm93c2VyID0gISEodHlwZW9mIHdpbmRvdyAhPT0gJ3VuZGVmaW5lZCcgJiYgdHlwZW9mIG5hdmlnYXRvciAhPT0gJ3VuZGVmaW5lZCcgJiYgd2luZG93LmRvY3VtZW50KSwKICAgICAgICBpc1dlYldvcmtlciA9ICFpc0Jyb3dzZXIgJiYgdHlwZW9mIGltcG9ydFNjcmlwdHMgIT09ICd1bmRlZmluZWQnLAogICAgICAgIC8vUFMzIGluZGljYXRlcyBsb2FkZWQgYW5kIGNvbXBsZXRlLCBidXQgbmVlZCB0byB3YWl0IGZvciBjb21wbGV0ZQogICAgICAgIC8vc3BlY2lmaWNhbGx5LiBTZXF1ZW5jZSBpcyAnbG9hZGluZycsICdsb2FkZWQnLCBleGVjdXRpb24sCiAgICAgICAgLy8gdGhlbiAnY29tcGxldGUnLiBUaGUgVUEgY2hlY2sgaXMgdW5mb3J0dW5hdGUsIGJ1dCBub3Qgc3VyZSBob3cKICAgICAgICAvL3RvIGZlYXR1cmUgdGVzdCB3L28gY2F1c2luZyBwZXJmIGlzc3Vlcy4KICAgICAgICByZWFkeVJlZ0V4cCA9IGlzQnJvd3NlciAmJiBuYXZpZ2F0b3IucGxhdGZvcm0gPT09ICdQTEFZU1RBVElPTiAzJyA/CiAgICAgICAgICAgICAgICAgICAgICAvXmNvbXBsZXRlJC8gOiAvXihjb21wbGV0ZXxsb2FkZWQpJC8sCiAgICAgICAgZGVmQ29udGV4dE5hbWUgPSAnXycsCiAgICAgICAgLy9PaCB0aGUgdHJhZ2VkeSwgZGV0ZWN0aW5nIG9wZXJhLiBTZWUgdGhlIHVzYWdlIG9mIGlzT3BlcmEgZm9yIHJlYXNvbi4KICAgICAgICBpc09wZXJhID0gdHlwZW9mIG9wZXJhICE9PSAndW5kZWZpbmVkJyAmJiBvcGVyYS50b1N0cmluZygpID09PSAnW29iamVjdCBPcGVyYV0nLAogICAgICAgIGNvbnRleHRzID0ge30sCiAgICAgICAgY2ZnID0ge30sCiAgICAgICAgZ2xvYmFsRGVmUXVldWUgPSBbXSwKICAgICAgICB1c2VJbnRlcmFjdGl2ZSA9IGZhbHNlOwoKICAgIGZ1bmN0aW9uIGlzRnVuY3Rpb24oaXQpIHsKICAgICAgICByZXR1cm4gb3N0cmluZy5jYWxsKGl0KSA9PT0gJ1tvYmplY3QgRnVuY3Rpb25dJzsKICAgIH0KCiAgICBmdW5jdGlvbiBpc0FycmF5KGl0KSB7CiAgICAgICAgcmV0dXJuIG9zdHJpbmcuY2FsbChpdCkgPT09ICdbb2JqZWN0IEFycmF5XSc7CiAgICB9CgogICAgLyoqCiAgICAgKiBIZWxwZXIgZnVuY3Rpb24gZm9yIGl0ZXJhdGluZyBvdmVyIGFuIGFycmF5LiBJZiB0aGUgZnVuYyByZXR1cm5zCiAgICAgKiBhIHRydWUgdmFsdWUsIGl0IHdpbGwgYnJlYWsgb3V0IG9mIHRoZSBsb29wLgogICAgICovCiAgICBmdW5jdGlvbiBlYWNoKGFyeSwgZnVuYykgewogICAgICAgIGlmIChhcnkpIHsKICAgICAgICAgICAgdmFyIGk7CiAgICAgICAgICAgIGZvciAoaSA9IDA7IGkgPCBhcnkubGVuZ3RoOyBpICs9IDEpIHsKICAgICAgICAgICAgICAgIGlmIChhcnlbaV0gJiYgZnVuYyhhcnlbaV0sIGksIGFyeSkpIHsKICAgICAgICAgICAgICAgICAgICBicmVhazsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgIH0KICAgIH0KCiAgICAvKioKICAgICAqIEhlbHBlciBmdW5jdGlvbiBmb3IgaXRlcmF0aW5nIG92ZXIgYW4gYXJyYXkgYmFja3dhcmRzLiBJZiB0aGUgZnVuYwogICAgICogcmV0dXJucyBhIHRydWUgdmFsdWUsIGl0IHdpbGwgYnJlYWsgb3V0IG9mIHRoZSBsb29wLgogICAgICovCiAgICBmdW5jdGlvbiBlYWNoUmV2ZXJzZShhcnksIGZ1bmMpIHsKICAgICAgICBpZiAoYXJ5KSB7CiAgICAgICAgICAgIHZhciBpOwogICAgICAgICAgICBmb3IgKGkgPSBhcnkubGVuZ3RoIC0gMTsgaSA+IC0xOyBpIC09IDEpIHsKICAgICAgICAgICAgICAgIGlmIChhcnlbaV0gJiYgZnVuYyhhcnlbaV0sIGksIGFyeSkpIHsKICAgICAgICAgICAgICAgICAgICBicmVhazsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgIH0KICAgIH0KCiAgICBmdW5jdGlvbiBoYXNQcm9wKG9iaiwgcHJvcCkgewogICAgICAgIHJldHVybiBoYXNPd24uY2FsbChvYmosIHByb3ApOwogICAgfQoKICAgIGZ1bmN0aW9uIGdldE93bihvYmosIHByb3ApIHsKICAgICAgICByZXR1cm4gaGFzUHJvcChvYmosIHByb3ApICYmIG9ialtwcm9wXTsKICAgIH0KCiAgICAvKioKICAgICAqIEN5Y2xlcyBvdmVyIHByb3BlcnRpZXMgaW4gYW4gb2JqZWN0IGFuZCBjYWxscyBhIGZ1bmN0aW9uIGZvciBlYWNoCiAgICAgKiBwcm9wZXJ0eSB2YWx1ZS4gSWYgdGhlIGZ1bmN0aW9uIHJldHVybnMgYSB0cnV0aHkgdmFsdWUsIHRoZW4gdGhlCiAgICAgKiBpdGVyYXRpb24gaXMgc3RvcHBlZC4KICAgICAqLwogICAgZnVuY3Rpb24gZWFjaFByb3Aob2JqLCBmdW5jKSB7CiAgICAgICAgdmFyIHByb3A7CiAgICAgICAgZm9yIChwcm9wIGluIG9iaikgewogICAgICAgICAgICBpZiAoaGFzUHJvcChvYmosIHByb3ApKSB7CiAgICAgICAgICAgICAgICBpZiAoZnVuYyhvYmpbcHJvcF0sIHByb3ApKSB7CiAgICAgICAgICAgICAgICAgICAgYnJlYWs7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICB9CgogICAgLyoqCiAgICAgKiBTaW1wbGUgZnVuY3Rpb24gdG8gbWl4IGluIHByb3BlcnRpZXMgZnJvbSBzb3VyY2UgaW50byB0YXJnZXQsCiAgICAgKiBidXQgb25seSBpZiB0YXJnZXQgZG9lcyBub3QgYWxyZWFkeSBoYXZlIGEgcHJvcGVydHkgb2YgdGhlIHNhbWUgbmFtZS4KICAgICAqLwogICAgZnVuY3Rpb24gbWl4aW4odGFyZ2V0LCBzb3VyY2UsIGZvcmNlLCBkZWVwU3RyaW5nTWl4aW4pIHsKICAgICAgICBpZiAoc291cmNlKSB7CiAgICAgICAgICAgIGVhY2hQcm9wKHNvdXJjZSwgZnVuY3Rpb24gKHZhbHVlLCBwcm9wKSB7CiAgICAgICAgICAgICAgICBpZiAoZm9yY2UgfHwgIWhhc1Byb3AodGFyZ2V0LCBwcm9wKSkgewogICAgICAgICAgICAgICAgICAgIGlmIChkZWVwU3RyaW5nTWl4aW4gJiYgdHlwZW9mIHZhbHVlID09PSAnb2JqZWN0JyAmJiB2YWx1ZSAmJgogICAgICAgICAgICAgICAgICAgICAgICAhaXNBcnJheSh2YWx1ZSkgJiYgIWlzRnVuY3Rpb24odmFsdWUpICYmCiAgICAgICAgICAgICAgICAgICAgICAgICEodmFsdWUgaW5zdGFuY2VvZiBSZWdFeHApKSB7CgogICAgICAgICAgICAgICAgICAgICAgICBpZiAoIXRhcmdldFtwcm9wXSkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgdGFyZ2V0W3Byb3BdID0ge307CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgbWl4aW4odGFyZ2V0W3Byb3BdLCB2YWx1ZSwgZm9yY2UsIGRlZXBTdHJpbmdNaXhpbik7CiAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgICAgICAgICAgICAgdGFyZ2V0W3Byb3BdID0gdmFsdWU7CiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9KTsKICAgICAgICB9CiAgICAgICAgcmV0dXJuIHRhcmdldDsKICAgIH0KCiAgICAvL1NpbWlsYXIgdG8gRnVuY3Rpb24ucHJvdG90eXBlLmJpbmQsIGJ1dCB0aGUgJ3RoaXMnIG9iamVjdCBpcyBzcGVjaWZpZWQKICAgIC8vZmlyc3QsIHNpbmNlIGl0IGlzIGVhc2llciB0byByZWFkL2ZpZ3VyZSBvdXQgd2hhdCAndGhpcycgd2lsbCBiZS4KICAgIGZ1bmN0aW9uIGJpbmQob2JqLCBmbikgewogICAgICAgIHJldHVybiBmdW5jdGlvbiAoKSB7CiAgICAgICAgICAgIHJldHVybiBmbi5hcHBseShvYmosIGFyZ3VtZW50cyk7CiAgICAgICAgfTsKICAgIH0KCiAgICBmdW5jdGlvbiBzY3JpcHRzKCkgewogICAgICAgIHJldHVybiBkb2N1bWVudC5nZXRFbGVtZW50c0J5VGFnTmFtZSgnc2NyaXB0Jyk7CiAgICB9CgogICAgZnVuY3Rpb24gZGVmYXVsdE9uRXJyb3IoZXJyKSB7CiAgICAgICAgdGhyb3cgZXJyOwogICAgfQoKICAgIC8vQWxsb3cgZ2V0dGluZyBhIGdsb2JhbCB0aGF0IGlzIGV4cHJlc3NlZCBpbgogICAgLy9kb3Qgbm90YXRpb24sIGxpa2UgJ2EuYi5jJy4KICAgIGZ1bmN0aW9uIGdldEdsb2JhbCh2YWx1ZSkgewogICAgICAgIGlmICghdmFsdWUpIHsKICAgICAgICAgICAgcmV0dXJuIHZhbHVlOwogICAgICAgIH0KICAgICAgICB2YXIgZyA9IGdsb2JhbDsKICAgICAgICBlYWNoKHZhbHVlLnNwbGl0KCcuJyksIGZ1bmN0aW9uIChwYXJ0KSB7CiAgICAgICAgICAgIGcgPSBnW3BhcnRdOwogICAgICAgIH0pOwogICAgICAgIHJldHVybiBnOwogICAgfQoKICAgIC8qKgogICAgICogQ29uc3RydWN0cyBhbiBlcnJvciB3aXRoIGEgcG9pbnRlciB0byBhbiBVUkwgd2l0aCBtb3JlIGluZm9ybWF0aW9uLgogICAgICogQHBhcmFtIHtTdHJpbmd9IGlkIHRoZSBlcnJvciBJRCB0aGF0IG1hcHMgdG8gYW4gSUQgb24gYSB3ZWIgcGFnZS4KICAgICAqIEBwYXJhbSB7U3RyaW5nfSBtZXNzYWdlIGh1bWFuIHJlYWRhYmxlIGVycm9yLgogICAgICogQHBhcmFtIHtFcnJvcn0gW2Vycl0gdGhlIG9yaWdpbmFsIGVycm9yLCBpZiB0aGVyZSBpcyBvbmUuCiAgICAgKgogICAgICogQHJldHVybnMge0Vycm9yfQogICAgICovCiAgICBmdW5jdGlvbiBtYWtlRXJyb3IoaWQsIG1zZywgZXJyLCByZXF1aXJlTW9kdWxlcykgewogICAgICAgIHZhciBlID0gbmV3IEVycm9yKG1zZyArICdcbmh0dHA6Ly9yZXF1aXJlanMub3JnL2RvY3MvZXJyb3JzLmh0bWwjJyArIGlkKTsKICAgICAgICBlLnJlcXVpcmVUeXBlID0gaWQ7CiAgICAgICAgZS5yZXF1aXJlTW9kdWxlcyA9IHJlcXVpcmVNb2R1bGVzOwogICAgICAgIGlmIChlcnIpIHsKICAgICAgICAgICAgZS5vcmlnaW5hbEVycm9yID0gZXJyOwogICAgICAgIH0KICAgICAgICByZXR1cm4gZTsKICAgIH0KCiAgICBpZiAodHlwZW9mIGRlZmluZSAhPT0gJ3VuZGVmaW5lZCcpIHsKICAgICAgICAvL0lmIGEgZGVmaW5lIGlzIGFscmVhZHkgaW4gcGxheSB2aWEgYW5vdGhlciBBTUQgbG9hZGVyLAogICAgICAgIC8vZG8gbm90IG92ZXJ3cml0ZS4KICAgICAgICByZXR1cm47CiAgICB9CgogICAgaWYgKHR5cGVvZiByZXF1aXJlanMgIT09ICd1bmRlZmluZWQnKSB7CiAgICAgICAgaWYgKGlzRnVuY3Rpb24ocmVxdWlyZWpzKSkgewogICAgICAgICAgICAvL0RvIG5vdCBvdmVyd3JpdGUgYW4gZXhpc3RpbmcgcmVxdWlyZWpzIGluc3RhbmNlLgogICAgICAgICAgICByZXR1cm47CiAgICAgICAgfQogICAgICAgIGNmZyA9IHJlcXVpcmVqczsKICAgICAgICByZXF1aXJlanMgPSB1bmRlZmluZWQ7CiAgICB9CgogICAgLy9BbGxvdyBmb3IgYSByZXF1aXJlIGNvbmZpZyBvYmplY3QKICAgIGlmICh0eXBlb2YgcmVxdWlyZSAhPT0gJ3VuZGVmaW5lZCcgJiYgIWlzRnVuY3Rpb24ocmVxdWlyZSkpIHsKICAgICAgICAvL2Fzc3VtZSBpdCBpcyBhIGNvbmZpZyBvYmplY3QuCiAgICAgICAgY2ZnID0gcmVxdWlyZTsKICAgICAgICByZXF1aXJlID0gdW5kZWZpbmVkOwogICAgfQoKICAgIGZ1bmN0aW9uIG5ld0NvbnRleHQoY29udGV4dE5hbWUpIHsKICAgICAgICB2YXIgaW5DaGVja0xvYWRlZCwgTW9kdWxlLCBjb250ZXh0LCBoYW5kbGVycywKICAgICAgICAgICAgY2hlY2tMb2FkZWRUaW1lb3V0SWQsCiAgICAgICAgICAgIGNvbmZpZyA9IHsKICAgICAgICAgICAgICAgIC8vRGVmYXVsdHMuIERvIG5vdCBzZXQgYSBkZWZhdWx0IGZvciBtYXAKICAgICAgICAgICAgICAgIC8vY29uZmlnIHRvIHNwZWVkIHVwIG5vcm1hbGl6ZSgpLCB3aGljaAogICAgICAgICAgICAgICAgLy93aWxsIHJ1biBmYXN0ZXIgaWYgdGhlcmUgaXMgbm8gZGVmYXVsdC4KICAgICAgICAgICAgICAgIHdhaXRTZWNvbmRzOiA3LAogICAgICAgICAgICAgICAgYmFzZVVybDogJy4vJywKICAgICAgICAgICAgICAgIHBhdGhzOiB7fSwKICAgICAgICAgICAgICAgIGJ1bmRsZXM6IHt9LAogICAgICAgICAgICAgICAgcGtnczoge30sCiAgICAgICAgICAgICAgICBzaGltOiB7fSwKICAgICAgICAgICAgICAgIGNvbmZpZzoge30KICAgICAgICAgICAgfSwKICAgICAgICAgICAgcmVnaXN0cnkgPSB7fSwKICAgICAgICAgICAgLy9yZWdpc3RyeSBvZiBqdXN0IGVuYWJsZWQgbW9kdWxlcywgdG8gc3BlZWQKICAgICAgICAgICAgLy9jeWNsZSBicmVha2luZyBjb2RlIHdoZW4gbG90cyBvZiBtb2R1bGVzCiAgICAgICAgICAgIC8vYXJlIHJlZ2lzdGVyZWQsIGJ1dCBub3QgYWN0aXZhdGVkLgogICAgICAgICAgICBlbmFibGVkUmVnaXN0cnkgPSB7fSwKICAgICAgICAgICAgdW5kZWZFdmVudHMgPSB7fSwKICAgICAgICAgICAgZGVmUXVldWUgPSBbXSwKICAgICAgICAgICAgZGVmaW5lZCA9IHt9LAogICAgICAgICAgICB1cmxGZXRjaGVkID0ge30sCiAgICAgICAgICAgIGJ1bmRsZXNNYXAgPSB7fSwKICAgICAgICAgICAgcmVxdWlyZUNvdW50ZXIgPSAxLAogICAgICAgICAgICB1bm5vcm1hbGl6ZWRDb3VudGVyID0gMTsKCiAgICAgICAgLyoqCiAgICAgICAgICogVHJpbXMgdGhlIC4gYW5kIC4uIGZyb20gYW4gYXJyYXkgb2YgcGF0aCBzZWdtZW50cy4KICAgICAgICAgKiBJdCB3aWxsIGtlZXAgYSBsZWFkaW5nIHBhdGggc2VnbWVudCBpZiBhIC4uIHdpbGwgYmVjb21lCiAgICAgICAgICogdGhlIGZpcnN0IHBhdGggc2VnbWVudCwgdG8gaGVscCB3aXRoIG1vZHVsZSBuYW1lIGxvb2t1cHMsCiAgICAgICAgICogd2hpY2ggYWN0IGxpa2UgcGF0aHMsIGJ1dCBjYW4gYmUgcmVtYXBwZWQuIEJ1dCB0aGUgZW5kIHJlc3VsdCwKICAgICAgICAgKiBhbGwgcGF0aHMgdGhhdCB1c2UgdGhpcyBmdW5jdGlvbiBzaG91bGQgbG9vayBub3JtYWxpemVkLgogICAgICAgICAqIE5PVEU6IHRoaXMgbWV0aG9kIE1PRElGSUVTIHRoZSBpbnB1dCBhcnJheS4KICAgICAgICAgKiBAcGFyYW0ge0FycmF5fSBhcnkgdGhlIGFycmF5IG9mIHBhdGggc2VnbWVudHMuCiAgICAgICAgICovCiAgICAgICAgZnVuY3Rpb24gdHJpbURvdHMoYXJ5KSB7CiAgICAgICAgICAgIHZhciBpLCBwYXJ0OwogICAgICAgICAgICBmb3IgKGkgPSAwOyBpIDwgYXJ5Lmxlbmd0aDsgaSsrKSB7CiAgICAgICAgICAgICAgICBwYXJ0ID0gYXJ5W2ldOwogICAgICAgICAgICAgICAgaWYgKHBhcnQgPT09ICcuJykgewogICAgICAgICAgICAgICAgICAgIGFyeS5zcGxpY2UoaSwgMSk7CiAgICAgICAgICAgICAgICAgICAgaSAtPSAxOwogICAgICAgICAgICAgICAgfSBlbHNlIGlmIChwYXJ0ID09PSAnLi4nKSB7CiAgICAgICAgICAgICAgICAgICAgLy8gSWYgYXQgdGhlIHN0YXJ0LCBvciBwcmV2aW91cyB2YWx1ZSBpcyBzdGlsbCAuLiwKICAgICAgICAgICAgICAgICAgICAvLyBrZWVwIHRoZW0gc28gdGhhdCB3aGVuIGNvbnZlcnRlZCB0byBhIHBhdGggaXQgbWF5CiAgICAgICAgICAgICAgICAgICAgLy8gc3RpbGwgd29yayB3aGVuIGNvbnZlcnRlZCB0byBhIHBhdGgsIGV2ZW4gdGhvdWdoCiAgICAgICAgICAgICAgICAgICAgLy8gYXMgYW4gSUQgaXQgaXMgbGVzcyB0aGFuIGlkZWFsLiBJbiBsYXJnZXIgcG9pbnQKICAgICAgICAgICAgICAgICAgICAvLyByZWxlYXNlcywgbWF5IGJlIGJldHRlciB0byBqdXN0IGtpY2sgb3V0IGFuIGVycm9yLgogICAgICAgICAgICAgICAgICAgIGlmIChpID09PSAwIHx8IChpID09PSAxICYmIGFyeVsyXSA9PT0gJy4uJykgfHwgYXJ5W2kgLSAxXSA9PT0gJy4uJykgewogICAgICAgICAgICAgICAgICAgICAgICBjb250aW51ZTsKICAgICAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKGkgPiAwKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIGFyeS5zcGxpY2UoaSAtIDEsIDIpOwogICAgICAgICAgICAgICAgICAgICAgICBpIC09IDI7CiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CiAgICAgICAgfQoKICAgICAgICAvKioKICAgICAgICAgKiBHaXZlbiBhIHJlbGF0aXZlIG1vZHVsZSBuYW1lLCBsaWtlIC4vc29tZXRoaW5nLCBub3JtYWxpemUgaXQgdG8KICAgICAgICAgKiBhIHJlYWwgbmFtZSB0aGF0IGNhbiBiZSBtYXBwZWQgdG8gYSBwYXRoLgogICAgICAgICAqIEBwYXJhbSB7U3RyaW5nfSBuYW1lIHRoZSByZWxhdGl2ZSBuYW1lCiAgICAgICAgICogQHBhcmFtIHtTdHJpbmd9IGJhc2VOYW1lIGEgcmVhbCBuYW1lIHRoYXQgdGhlIG5hbWUgYXJnIGlzIHJlbGF0aXZlCiAgICAgICAgICogdG8uCiAgICAgICAgICogQHBhcmFtIHtCb29sZWFufSBhcHBseU1hcCBhcHBseSB0aGUgbWFwIGNvbmZpZyB0byB0aGUgdmFsdWUuIFNob3VsZAogICAgICAgICAqIG9ubHkgYmUgZG9uZSBpZiB0aGlzIG5vcm1hbGl6YXRpb24gaXMgZm9yIGEgZGVwZW5kZW5jeSBJRC4KICAgICAgICAgKiBAcmV0dXJucyB7U3RyaW5nfSBub3JtYWxpemVkIG5hbWUKICAgICAgICAgKi8KICAgICAgICBmdW5jdGlvbiBub3JtYWxpemUobmFtZSwgYmFzZU5hbWUsIGFwcGx5TWFwKSB7CiAgICAgICAgICAgIHZhciBwa2dNYWluLCBtYXBWYWx1ZSwgbmFtZVBhcnRzLCBpLCBqLCBuYW1lU2VnbWVudCwgbGFzdEluZGV4LAogICAgICAgICAgICAgICAgZm91bmRNYXAsIGZvdW5kSSwgZm91bmRTdGFyTWFwLCBzdGFySSwgbm9ybWFsaXplZEJhc2VQYXJ0cywKICAgICAgICAgICAgICAgIGJhc2VQYXJ0cyA9IChiYXNlTmFtZSAmJiBiYXNlTmFtZS5zcGxpdCgnLycpKSwKICAgICAgICAgICAgICAgIG1hcCA9IGNvbmZpZy5tYXAsCiAgICAgICAgICAgICAgICBzdGFyTWFwID0gbWFwICYmIG1hcFsnKiddOwoKICAgICAgICAgICAgLy9BZGp1c3QgYW55IHJlbGF0aXZlIHBhdGhzLgogICAgICAgICAgICBpZiAobmFtZSkgewogICAgICAgICAgICAgICAgbmFtZSA9IG5hbWUuc3BsaXQoJy8nKTsKICAgICAgICAgICAgICAgIGxhc3RJbmRleCA9IG5hbWUubGVuZ3RoIC0gMTsKCiAgICAgICAgICAgICAgICAvLyBJZiB3YW50aW5nIG5vZGUgSUQgY29tcGF0aWJpbGl0eSwgc3RyaXAgLmpzIGZyb20gZW5kCiAgICAgICAgICAgICAgICAvLyBvZiBJRHMuIEhhdmUgdG8gZG8gdGhpcyBoZXJlLCBhbmQgbm90IGluIG5hbWVUb1VybAogICAgICAgICAgICAgICAgLy8gYmVjYXVzZSBub2RlIGFsbG93cyBlaXRoZXIgLmpzIG9yIG5vbiAuanMgdG8gbWFwCiAgICAgICAgICAgICAgICAvLyB0byBzYW1lIGZpbGUuCiAgICAgICAgICAgICAgICBpZiAoY29uZmlnLm5vZGVJZENvbXBhdCAmJiBqc1N1ZmZpeFJlZ0V4cC50ZXN0KG5hbWVbbGFzdEluZGV4XSkpIHsKICAgICAgICAgICAgICAgICAgICBuYW1lW2xhc3RJbmRleF0gPSBuYW1lW2xhc3RJbmRleF0ucmVwbGFjZShqc1N1ZmZpeFJlZ0V4cCwgJycpOwogICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgIC8vIFN0YXJ0cyB3aXRoIGEgJy4nIHNvIG5lZWQgdGhlIGJhc2VOYW1lCiAgICAgICAgICAgICAgICBpZiAobmFtZVswXS5jaGFyQXQoMCkgPT09ICcuJyAmJiBiYXNlUGFydHMpIHsKICAgICAgICAgICAgICAgICAgICAvL0NvbnZlcnQgYmFzZU5hbWUgdG8gYXJyYXksIGFuZCBsb3Agb2ZmIHRoZSBsYXN0IHBhcnQsCiAgICAgICAgICAgICAgICAgICAgLy9zbyB0aGF0IC4gbWF0Y2hlcyB0aGF0ICdkaXJlY3RvcnknIGFuZCBub3QgbmFtZSBvZiB0aGUgYmFzZU5hbWUncwogICAgICAgICAgICAgICAgICAgIC8vbW9kdWxlLiBGb3IgaW5zdGFuY2UsIGJhc2VOYW1lIG9mICdvbmUvdHdvL3RocmVlJywgbWFwcyB0bwogICAgICAgICAgICAgICAgICAgIC8vJ29uZS90d28vdGhyZWUuanMnLCBidXQgd2Ugd2FudCB0aGUgZGlyZWN0b3J5LCAnb25lL3R3bycgZm9yCiAgICAgICAgICAgICAgICAgICAgLy90aGlzIG5vcm1hbGl6YXRpb24uCiAgICAgICAgICAgICAgICAgICAgbm9ybWFsaXplZEJhc2VQYXJ0cyA9IGJhc2VQYXJ0cy5zbGljZSgwLCBiYXNlUGFydHMubGVuZ3RoIC0gMSk7CiAgICAgICAgICAgICAgICAgICAgbmFtZSA9IG5vcm1hbGl6ZWRCYXNlUGFydHMuY29uY2F0KG5hbWUpOwogICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgIHRyaW1Eb3RzKG5hbWUpOwogICAgICAgICAgICAgICAgbmFtZSA9IG5hbWUuam9pbignLycpOwogICAgICAgICAgICB9CgogICAgICAgICAgICAvL0FwcGx5IG1hcCBjb25maWcgaWYgYXZhaWxhYmxlLgogICAgICAgICAgICBpZiAoYXBwbHlNYXAgJiYgbWFwICYmIChiYXNlUGFydHMgfHwgc3Rhck1hcCkpIHsKICAgICAgICAgICAgICAgIG5hbWVQYXJ0cyA9IG5hbWUuc3BsaXQoJy8nKTsKCiAgICAgICAgICAgICAgICBvdXRlckxvb3A6IGZvciAoaSA9IG5hbWVQYXJ0cy5sZW5ndGg7IGkgPiAwOyBpIC09IDEpIHsKICAgICAgICAgICAgICAgICAgICBuYW1lU2VnbWVudCA9IG5hbWVQYXJ0cy5zbGljZSgwLCBpKS5qb2luKCcvJyk7CgogICAgICAgICAgICAgICAgICAgIGlmIChiYXNlUGFydHMpIHsKICAgICAgICAgICAgICAgICAgICAgICAgLy9GaW5kIHRoZSBsb25nZXN0IGJhc2VOYW1lIHNlZ21lbnQgbWF0Y2ggaW4gdGhlIGNvbmZpZy4KICAgICAgICAgICAgICAgICAgICAgICAgLy9TbywgZG8gam9pbnMgb24gdGhlIGJpZ2dlc3QgdG8gc21hbGxlc3QgbGVuZ3RocyBvZiBiYXNlUGFydHMuCiAgICAgICAgICAgICAgICAgICAgICAgIGZvciAoaiA9IGJhc2VQYXJ0cy5sZW5ndGg7IGogPiAwOyBqIC09IDEpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1hcFZhbHVlID0gZ2V0T3duKG1hcCwgYmFzZVBhcnRzLnNsaWNlKDAsIGopLmpvaW4oJy8nKSk7CgogICAgICAgICAgICAgICAgICAgICAgICAgICAgLy9iYXNlTmFtZSBzZWdtZW50IGhhcyBjb25maWcsIGZpbmQgaWYgaXQgaGFzIG9uZSBmb3IKICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vdGhpcyBuYW1lLgogICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKG1hcFZhbHVlKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWFwVmFsdWUgPSBnZXRPd24obWFwVmFsdWUsIG5hbWVTZWdtZW50KTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAobWFwVmFsdWUpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy9NYXRjaCwgdXBkYXRlIG5hbWUgdG8gdGhlIG5ldyB2YWx1ZS4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZm91bmRNYXAgPSBtYXBWYWx1ZTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZm91bmRJID0gaTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWsgb3V0ZXJMb29wOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICAgICAgLy9DaGVjayBmb3IgYSBzdGFyIG1hcCBtYXRjaCwgYnV0IGp1c3QgaG9sZCBvbiB0byBpdCwKICAgICAgICAgICAgICAgICAgICAvL2lmIHRoZXJlIGlzIGEgc2hvcnRlciBzZWdtZW50IG1hdGNoIGxhdGVyIGluIGEgbWF0Y2hpbmcKICAgICAgICAgICAgICAgICAgICAvL2NvbmZpZywgdGhlbiBmYXZvciBvdmVyIHRoaXMgc3RhciBtYXAuCiAgICAgICAgICAgICAgICAgICAgaWYgKCFmb3VuZFN0YXJNYXAgJiYgc3Rhck1hcCAmJiBnZXRPd24oc3Rhck1hcCwgbmFtZVNlZ21lbnQpKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIGZvdW5kU3Rhck1hcCA9IGdldE93bihzdGFyTWFwLCBuYW1lU2VnbWVudCk7CiAgICAgICAgICAgICAgICAgICAgICAgIHN0YXJJID0gaTsKICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgaWYgKCFmb3VuZE1hcCAmJiBmb3VuZFN0YXJNYXApIHsKICAgICAgICAgICAgICAgICAgICBmb3VuZE1hcCA9IGZvdW5kU3Rhck1hcDsKICAgICAgICAgICAgICAgICAgICBmb3VuZEkgPSBzdGFySTsKICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICBpZiAoZm91bmRNYXApIHsKICAgICAgICAgICAgICAgICAgICBuYW1lUGFydHMuc3BsaWNlKDAsIGZvdW5kSSwgZm91bmRNYXApOwogICAgICAgICAgICAgICAgICAgIG5hbWUgPSBuYW1lUGFydHMuam9pbignLycpOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CgogICAgICAgICAgICAvLyBJZiB0aGUgbmFtZSBwb2ludHMgdG8gYSBwYWNrYWdlJ3MgbmFtZSwgdXNlCiAgICAgICAgICAgIC8vIHRoZSBwYWNrYWdlIG1haW4gaW5zdGVhZC4KICAgICAgICAgICAgcGtnTWFpbiA9IGdldE93bihjb25maWcucGtncywgbmFtZSk7CgogICAgICAgICAgICByZXR1cm4gcGtnTWFpbiA/IHBrZ01haW4gOiBuYW1lOwogICAgICAgIH0KCiAgICAgICAgZnVuY3Rpb24gcmVtb3ZlU2NyaXB0KG5hbWUpIHsKICAgICAgICAgICAgaWYgKGlzQnJvd3NlcikgewogICAgICAgICAgICAgICAgZWFjaChzY3JpcHRzKCksIGZ1bmN0aW9uIChzY3JpcHROb2RlKSB7CiAgICAgICAgICAgICAgICAgICAgaWYgKHNjcmlwdE5vZGUuZ2V0QXR0cmlidXRlKCdkYXRhLXJlcXVpcmVtb2R1bGUnKSA9PT0gbmFtZSAmJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgc2NyaXB0Tm9kZS5nZXRBdHRyaWJ1dGUoJ2RhdGEtcmVxdWlyZWNvbnRleHQnKSA9PT0gY29udGV4dC5jb250ZXh0TmFtZSkgewogICAgICAgICAgICAgICAgICAgICAgICBzY3JpcHROb2RlLnBhcmVudE5vZGUucmVtb3ZlQ2hpbGQoc2NyaXB0Tm9kZSk7CiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiB0cnVlOwogICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIH0pOwogICAgICAgICAgICB9CiAgICAgICAgfQoKICAgICAgICBmdW5jdGlvbiBoYXNQYXRoRmFsbGJhY2soaWQpIHsKICAgICAgICAgICAgdmFyIHBhdGhDb25maWcgPSBnZXRPd24oY29uZmlnLnBhdGhzLCBpZCk7CiAgICAgICAgICAgIGlmIChwYXRoQ29uZmlnICYmIGlzQXJyYXkocGF0aENvbmZpZykgJiYgcGF0aENvbmZpZy5sZW5ndGggPiAxKSB7CiAgICAgICAgICAgICAgICAvL1BvcCBvZmYgdGhlIGZpcnN0IGFycmF5IHZhbHVlLCBzaW5jZSBpdCBmYWlsZWQsIGFuZAogICAgICAgICAgICAgICAgLy9yZXRyeQogICAgICAgICAgICAgICAgcGF0aENvbmZpZy5zaGlmdCgpOwogICAgICAgICAgICAgICAgY29udGV4dC5yZXF1aXJlLnVuZGVmKGlkKTsKCiAgICAgICAgICAgICAgICAvL0N1c3RvbSByZXF1aXJlIHRoYXQgZG9lcyBub3QgZG8gbWFwIHRyYW5zbGF0aW9uLCBzaW5jZQogICAgICAgICAgICAgICAgLy9JRCBpcyAiYWJzb2x1dGUiLCBhbHJlYWR5IG1hcHBlZC9yZXNvbHZlZC4KICAgICAgICAgICAgICAgIGNvbnRleHQubWFrZVJlcXVpcmUobnVsbCwgewogICAgICAgICAgICAgICAgICAgIHNraXBNYXA6IHRydWUKICAgICAgICAgICAgICAgIH0pKFtpZF0pOwoKICAgICAgICAgICAgICAgIHJldHVybiB0cnVlOwogICAgICAgICAgICB9CiAgICAgICAgfQoKICAgICAgICAvL1R1cm5zIGEgcGx1Z2luIXJlc291cmNlIHRvIFtwbHVnaW4sIHJlc291cmNlXQogICAgICAgIC8vd2l0aCB0aGUgcGx1Z2luIGJlaW5nIHVuZGVmaW5lZCBpZiB0aGUgbmFtZQogICAgICAgIC8vZGlkIG5vdCBoYXZlIGEgcGx1Z2luIHByZWZpeC4KICAgICAgICBmdW5jdGlvbiBzcGxpdFByZWZpeChuYW1lKSB7CiAgICAgICAgICAgIHZhciBwcmVmaXgsCiAgICAgICAgICAgICAgICBpbmRleCA9IG5hbWUgPyBuYW1lLmluZGV4T2YoJyEnKSA6IC0xOwogICAgICAgICAgICBpZiAoaW5kZXggPiAtMSkgewogICAgICAgICAgICAgICAgcHJlZml4ID0gbmFtZS5zdWJzdHJpbmcoMCwgaW5kZXgpOwogICAgICAgICAgICAgICAgbmFtZSA9IG5hbWUuc3Vic3RyaW5nKGluZGV4ICsgMSwgbmFtZS5sZW5ndGgpOwogICAgICAgICAgICB9CiAgICAgICAgICAgIHJldHVybiBbcHJlZml4LCBuYW1lXTsKICAgICAgICB9CgogICAgICAgIC8qKgogICAgICAgICAqIENyZWF0ZXMgYSBtb2R1bGUgbWFwcGluZyB0aGF0IGluY2x1ZGVzIHBsdWdpbiBwcmVmaXgsIG1vZHVsZQogICAgICAgICAqIG5hbWUsIGFuZCBwYXRoLiBJZiBwYXJlbnRNb2R1bGVNYXAgaXMgcHJvdmlkZWQgaXQgd2lsbAogICAgICAgICAqIGFsc28gbm9ybWFsaXplIHRoZSBuYW1lIHZpYSByZXF1aXJlLm5vcm1hbGl6ZSgpCiAgICAgICAgICoKICAgICAgICAgKiBAcGFyYW0ge1N0cmluZ30gbmFtZSB0aGUgbW9kdWxlIG5hbWUKICAgICAgICAgKiBAcGFyYW0ge1N0cmluZ30gW3BhcmVudE1vZHVsZU1hcF0gcGFyZW50IG1vZHVsZSBtYXAKICAgICAgICAgKiBmb3IgdGhlIG1vZHVsZSBuYW1lLCB1c2VkIHRvIHJlc29sdmUgcmVsYXRpdmUgbmFtZXMuCiAgICAgICAgICogQHBhcmFtIHtCb29sZWFufSBpc05vcm1hbGl6ZWQ6IGlzIHRoZSBJRCBhbHJlYWR5IG5vcm1hbGl6ZWQuCiAgICAgICAgICogVGhpcyBpcyB0cnVlIGlmIHRoaXMgY2FsbCBpcyBkb25lIGZvciBhIGRlZmluZSgpIG1vZHVsZSBJRC4KICAgICAgICAgKiBAcGFyYW0ge0Jvb2xlYW59IGFwcGx5TWFwOiBhcHBseSB0aGUgbWFwIGNvbmZpZyB0byB0aGUgSUQuCiAgICAgICAgICogU2hvdWxkIG9ubHkgYmUgdHJ1ZSBpZiB0aGlzIG1hcCBpcyBmb3IgYSBkZXBlbmRlbmN5LgogICAgICAgICAqCiAgICAgICAgICogQHJldHVybnMge09iamVjdH0KICAgICAgICAgKi8KICAgICAgICBmdW5jdGlvbiBtYWtlTW9kdWxlTWFwKG5hbWUsIHBhcmVudE1vZHVsZU1hcCwgaXNOb3JtYWxpemVkLCBhcHBseU1hcCkgewogICAgICAgICAgICB2YXIgdXJsLCBwbHVnaW5Nb2R1bGUsIHN1ZmZpeCwgbmFtZVBhcnRzLAogICAgICAgICAgICAgICAgcHJlZml4ID0gbnVsbCwKICAgICAgICAgICAgICAgIHBhcmVudE5hbWUgPSBwYXJlbnRNb2R1bGVNYXAgPyBwYXJlbnRNb2R1bGVNYXAubmFtZSA6IG51bGwsCiAgICAgICAgICAgICAgICBvcmlnaW5hbE5hbWUgPSBuYW1lLAogICAgICAgICAgICAgICAgaXNEZWZpbmUgPSB0cnVlLAogICAgICAgICAgICAgICAgbm9ybWFsaXplZE5hbWUgPSAnJzsKCiAgICAgICAgICAgIC8vSWYgbm8gbmFtZSwgdGhlbiBpdCBtZWFucyBpdCBpcyBhIHJlcXVpcmUgY2FsbCwgZ2VuZXJhdGUgYW4KICAgICAgICAgICAgLy9pbnRlcm5hbCBuYW1lLgogICAgICAgICAgICBpZiAoIW5hbWUpIHsKICAgICAgICAgICAgICAgIGlzRGVmaW5lID0gZmFsc2U7CiAgICAgICAgICAgICAgICBuYW1lID0gJ19AcicgKyAocmVxdWlyZUNvdW50ZXIgKz0gMSk7CiAgICAgICAgICAgIH0KCiAgICAgICAgICAgIG5hbWVQYXJ0cyA9IHNwbGl0UHJlZml4KG5hbWUpOwogICAgICAgICAgICBwcmVmaXggPSBuYW1lUGFydHNbMF07CiAgICAgICAgICAgIG5hbWUgPSBuYW1lUGFydHNbMV07CgogICAgICAgICAgICBpZiAocHJlZml4KSB7CiAgICAgICAgICAgICAgICBwcmVmaXggPSBub3JtYWxpemUocHJlZml4LCBwYXJlbnROYW1lLCBhcHBseU1hcCk7CiAgICAgICAgICAgICAgICBwbH
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment