Skip to content

Instantly share code, notes, and snippets.

@namdoan194
Last active June 30, 2020 12:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save namdoan194/f11ac92802eac0c48bf18fa8cfc42fa8 to your computer and use it in GitHub Desktop.
Save namdoan194/f11ac92802eac0c48bf18fa8cfc42fa8 to your computer and use it in GitHub Desktop.
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"metadata": {},
"cell_type": "code",
"source": "import pandas as pd\nimport numpy as np\nimport requests\nfrom bs4 import BeautifulSoup\nimport os\nfrom sklearn.cluster import KMeans \nfrom geopy.geocoders import Nominatim \nimport matplotlib.cm as cm\nimport matplotlib.colors as colors\nimport matplotlib.pyplot as plt\n%matplotlib inline\n\nprint('Libraries imported.')",
"execution_count": 1,
"outputs": [
{
"output_type": "stream",
"text": "Libraries imported.\n",
"name": "stdout"
}
]
},
{
"metadata": {},
"cell_type": "markdown",
"source": "1. Dowload and Explore Dataset"
},
{
"metadata": {},
"cell_type": "code",
"source": "List_url='https://en.wikipedia.org/wiki/List_of_postal_codes_of_Canada:_M'\nsource = requests.get(List_url).text",
"execution_count": 2,
"outputs": []
},
{
"metadata": {},
"cell_type": "code",
"source": "soup = BeautifulSoup(source, 'xml')\ntable=soup.find('table')\ncolumn_names=['Postalcode','Borough','Neighborhood']\ndf = pd.DataFrame(columns=column_names)",
"execution_count": 3,
"outputs": []
},
{
"metadata": {},
"cell_type": "code",
"source": "for tr_cell in table.find_all('tr'):\n row_data=[]\n for td_cell in tr_cell.find_all('td'):\n row_data.append(td_cell.text.strip())\n if len(row_data)==3:\n df.loc[len(df)] = row_data\n df.head()",
"execution_count": 4,
"outputs": []
},
{
"metadata": {},
"cell_type": "code",
"source": "df=df[df['Borough']!='Not assigned']",
"execution_count": 5,
"outputs": []
},
{
"metadata": {},
"cell_type": "code",
"source": "df.head()",
"execution_count": 6,
"outputs": [
{
"output_type": "execute_result",
"execution_count": 6,
"data": {
"text/plain": " Postalcode Borough Neighborhood\n2 M3A North York Parkwoods\n3 M4A North York Victoria Village\n4 M5A Downtown Toronto Regent Park, Harbourfront\n5 M6A North York Lawrence Manor, Lawrence Heights\n6 M7A Downtown Toronto Queen's Park, Ontario Provincial Government",
"text/html": "<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>Postalcode</th>\n <th>Borough</th>\n <th>Neighborhood</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <th>2</th>\n <td>M3A</td>\n <td>North York</td>\n <td>Parkwoods</td>\n </tr>\n <tr>\n <th>3</th>\n <td>M4A</td>\n <td>North York</td>\n <td>Victoria Village</td>\n </tr>\n <tr>\n <th>4</th>\n <td>M5A</td>\n <td>Downtown Toronto</td>\n <td>Regent Park, Harbourfront</td>\n </tr>\n <tr>\n <th>5</th>\n <td>M6A</td>\n <td>North York</td>\n <td>Lawrence Manor, Lawrence Heights</td>\n </tr>\n <tr>\n <th>6</th>\n <td>M7A</td>\n <td>Downtown Toronto</td>\n <td>Queen's Park, Ontario Provincial Government</td>\n </tr>\n </tbody>\n</table>\n</div>"
},
"metadata": {}
}
]
},
{
"metadata": {},
"cell_type": "code",
"source": "temp_df=df.groupby('Postalcode')['Neighborhood'].apply(lambda x: \"%s\" % ', '.join(x))\ntemp_df=temp_df.reset_index(drop=False)\ntemp_df.rename(columns={'Neighborhood':'Neighborhood_joined'},inplace=True)",
"execution_count": 7,
"outputs": []
},
{
"metadata": {},
"cell_type": "code",
"source": "df_merge = pd.merge(df, temp_df, on='Postalcode')",
"execution_count": 8,
"outputs": []
},
{
"metadata": {},
"cell_type": "code",
"source": "df_merge.drop(['Neighborhood'],axis=1,inplace=True)",
"execution_count": 9,
"outputs": []
},
{
"metadata": {},
"cell_type": "code",
"source": "df_merge.drop_duplicates(inplace=True)",
"execution_count": 10,
"outputs": []
},
{
"metadata": {},
"cell_type": "code",
"source": "df_merge.rename(columns={'Neighborhood_joined':'Neighborhood'},inplace=True)",
"execution_count": 11,
"outputs": []
},
{
"metadata": {},
"cell_type": "code",
"source": "df_merge.head()",
"execution_count": 12,
"outputs": [
{
"output_type": "execute_result",
"execution_count": 12,
"data": {
"text/plain": " Postalcode Borough Neighborhood\n0 M3A North York Parkwoods\n1 M4A North York Victoria Village\n2 M5A Downtown Toronto Regent Park, Harbourfront\n3 M6A North York Lawrence Manor, Lawrence Heights\n4 M7A Downtown Toronto Queen's Park, Ontario Provincial Government",
"text/html": "<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>Postalcode</th>\n <th>Borough</th>\n <th>Neighborhood</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <th>0</th>\n <td>M3A</td>\n <td>North York</td>\n <td>Parkwoods</td>\n </tr>\n <tr>\n <th>1</th>\n <td>M4A</td>\n <td>North York</td>\n <td>Victoria Village</td>\n </tr>\n <tr>\n <th>2</th>\n <td>M5A</td>\n <td>Downtown Toronto</td>\n <td>Regent Park, Harbourfront</td>\n </tr>\n <tr>\n <th>3</th>\n <td>M6A</td>\n <td>North York</td>\n <td>Lawrence Manor, Lawrence Heights</td>\n </tr>\n <tr>\n <th>4</th>\n <td>M7A</td>\n <td>Downtown Toronto</td>\n <td>Queen's Park, Ontario Provincial Government</td>\n </tr>\n </tbody>\n</table>\n</div>"
},
"metadata": {}
}
]
},
{
"metadata": {},
"cell_type": "code",
"source": "df_merge.shape",
"execution_count": 13,
"outputs": [
{
"output_type": "execute_result",
"execution_count": 13,
"data": {
"text/plain": "(103, 3)"
},
"metadata": {}
}
]
},
{
"metadata": {},
"cell_type": "code",
"source": "def get_geocode(postal_code):\n # initialize your variable to None\n lat_lng_coords = None\n while(lat_lng_coords is None):\n g = geocoder.google('{}, Toronto, Ontario'.format(postal_code))\n lat_lng_coords = g.latlng\n latitude = lat_lng_coords[0]\n longitude = lat_lng_coords[1]\n return latitude,longitude",
"execution_count": 14,
"outputs": []
},
{
"metadata": {},
"cell_type": "code",
"source": "geo_df=pd.read_csv('http://cocl.us/Geospatial_data')",
"execution_count": 15,
"outputs": []
},
{
"metadata": {},
"cell_type": "code",
"source": "geo_df.head()",
"execution_count": 16,
"outputs": [
{
"output_type": "execute_result",
"execution_count": 16,
"data": {
"text/plain": " Postal Code Latitude Longitude\n0 M1B 43.806686 -79.194353\n1 M1C 43.784535 -79.160497\n2 M1E 43.763573 -79.188711\n3 M1G 43.770992 -79.216917\n4 M1H 43.773136 -79.239476",
"text/html": "<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>Postal Code</th>\n <th>Latitude</th>\n <th>Longitude</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <th>0</th>\n <td>M1B</td>\n <td>43.806686</td>\n <td>-79.194353</td>\n </tr>\n <tr>\n <th>1</th>\n <td>M1C</td>\n <td>43.784535</td>\n <td>-79.160497</td>\n </tr>\n <tr>\n <th>2</th>\n <td>M1E</td>\n <td>43.763573</td>\n <td>-79.188711</td>\n </tr>\n <tr>\n <th>3</th>\n <td>M1G</td>\n <td>43.770992</td>\n <td>-79.216917</td>\n </tr>\n <tr>\n <th>4</th>\n <td>M1H</td>\n <td>43.773136</td>\n <td>-79.239476</td>\n </tr>\n </tbody>\n</table>\n</div>"
},
"metadata": {}
}
]
},
{
"metadata": {},
"cell_type": "code",
"source": "geo_df.rename(columns={'Postal Code':'Postalcode'},inplace=True)\ngeo_merged = pd.merge(geo_df, df_merge, on='Postalcode')",
"execution_count": 17,
"outputs": []
},
{
"metadata": {},
"cell_type": "code",
"source": "geo_merged.head()",
"execution_count": 18,
"outputs": [
{
"output_type": "execute_result",
"execution_count": 18,
"data": {
"text/plain": " Postalcode Latitude Longitude Borough \\\n0 M1B 43.806686 -79.194353 Scarborough \n1 M1C 43.784535 -79.160497 Scarborough \n2 M1E 43.763573 -79.188711 Scarborough \n3 M1G 43.770992 -79.216917 Scarborough \n4 M1H 43.773136 -79.239476 Scarborough \n\n Neighborhood \n0 Malvern, Rouge \n1 Rouge Hill, Port Union, Highland Creek \n2 Guildwood, Morningside, West Hill \n3 Woburn \n4 Cedarbrae ",
"text/html": "<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>Postalcode</th>\n <th>Latitude</th>\n <th>Longitude</th>\n <th>Borough</th>\n <th>Neighborhood</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <th>0</th>\n <td>M1B</td>\n <td>43.806686</td>\n <td>-79.194353</td>\n <td>Scarborough</td>\n <td>Malvern, Rouge</td>\n </tr>\n <tr>\n <th>1</th>\n <td>M1C</td>\n <td>43.784535</td>\n <td>-79.160497</td>\n <td>Scarborough</td>\n <td>Rouge Hill, Port Union, Highland Creek</td>\n </tr>\n <tr>\n <th>2</th>\n <td>M1E</td>\n <td>43.763573</td>\n <td>-79.188711</td>\n <td>Scarborough</td>\n <td>Guildwood, Morningside, West Hill</td>\n </tr>\n <tr>\n <th>3</th>\n <td>M1G</td>\n <td>43.770992</td>\n <td>-79.216917</td>\n <td>Scarborough</td>\n <td>Woburn</td>\n </tr>\n <tr>\n <th>4</th>\n <td>M1H</td>\n <td>43.773136</td>\n <td>-79.239476</td>\n <td>Scarborough</td>\n <td>Cedarbrae</td>\n </tr>\n </tbody>\n</table>\n</div>"
},
"metadata": {}
}
]
},
{
"metadata": {},
"cell_type": "code",
"source": "geo_data=geo_merged[['Postalcode','Borough','Neighborhood','Latitude','Longitude']]\ngeo_data.head()",
"execution_count": 19,
"outputs": [
{
"output_type": "execute_result",
"execution_count": 19,
"data": {
"text/plain": " Postalcode Borough Neighborhood Latitude \\\n0 M1B Scarborough Malvern, Rouge 43.806686 \n1 M1C Scarborough Rouge Hill, Port Union, Highland Creek 43.784535 \n2 M1E Scarborough Guildwood, Morningside, West Hill 43.763573 \n3 M1G Scarborough Woburn 43.770992 \n4 M1H Scarborough Cedarbrae 43.773136 \n\n Longitude \n0 -79.194353 \n1 -79.160497 \n2 -79.188711 \n3 -79.216917 \n4 -79.239476 ",
"text/html": "<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>Postalcode</th>\n <th>Borough</th>\n <th>Neighborhood</th>\n <th>Latitude</th>\n <th>Longitude</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <th>0</th>\n <td>M1B</td>\n <td>Scarborough</td>\n <td>Malvern, Rouge</td>\n <td>43.806686</td>\n <td>-79.194353</td>\n </tr>\n <tr>\n <th>1</th>\n <td>M1C</td>\n <td>Scarborough</td>\n <td>Rouge Hill, Port Union, Highland Creek</td>\n <td>43.784535</td>\n <td>-79.160497</td>\n </tr>\n <tr>\n <th>2</th>\n <td>M1E</td>\n <td>Scarborough</td>\n <td>Guildwood, Morningside, West Hill</td>\n <td>43.763573</td>\n <td>-79.188711</td>\n </tr>\n <tr>\n <th>3</th>\n <td>M1G</td>\n <td>Scarborough</td>\n <td>Woburn</td>\n <td>43.770992</td>\n <td>-79.216917</td>\n </tr>\n <tr>\n <th>4</th>\n <td>M1H</td>\n <td>Scarborough</td>\n <td>Cedarbrae</td>\n <td>43.773136</td>\n <td>-79.239476</td>\n </tr>\n </tbody>\n</table>\n</div>"
},
"metadata": {}
}
]
},
{
"metadata": {},
"cell_type": "code",
"source": "toronto_data=geo_data[geo_data['Borough'].str.contains(\"Toronto\")]\ntoronto_data.head()",
"execution_count": 20,
"outputs": [
{
"output_type": "execute_result",
"execution_count": 20,
"data": {
"text/plain": " Postalcode Borough Neighborhood Latitude \\\n37 M4E East Toronto The Beaches 43.676357 \n41 M4K East Toronto The Danforth West, Riverdale 43.679557 \n42 M4L East Toronto India Bazaar, The Beaches West 43.668999 \n43 M4M East Toronto Studio District 43.659526 \n44 M4N Central Toronto Lawrence Park 43.728020 \n\n Longitude \n37 -79.293031 \n41 -79.352188 \n42 -79.315572 \n43 -79.340923 \n44 -79.388790 ",
"text/html": "<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>Postalcode</th>\n <th>Borough</th>\n <th>Neighborhood</th>\n <th>Latitude</th>\n <th>Longitude</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <th>37</th>\n <td>M4E</td>\n <td>East Toronto</td>\n <td>The Beaches</td>\n <td>43.676357</td>\n <td>-79.293031</td>\n </tr>\n <tr>\n <th>41</th>\n <td>M4K</td>\n <td>East Toronto</td>\n <td>The Danforth West, Riverdale</td>\n <td>43.679557</td>\n <td>-79.352188</td>\n </tr>\n <tr>\n <th>42</th>\n <td>M4L</td>\n <td>East Toronto</td>\n <td>India Bazaar, The Beaches West</td>\n <td>43.668999</td>\n <td>-79.315572</td>\n </tr>\n <tr>\n <th>43</th>\n <td>M4M</td>\n <td>East Toronto</td>\n <td>Studio District</td>\n <td>43.659526</td>\n <td>-79.340923</td>\n </tr>\n <tr>\n <th>44</th>\n <td>M4N</td>\n <td>Central Toronto</td>\n <td>Lawrence Park</td>\n <td>43.728020</td>\n <td>-79.388790</td>\n </tr>\n </tbody>\n</table>\n</div>"
},
"metadata": {}
}
]
},
{
"metadata": {},
"cell_type": "code",
"source": "toronto_data.groupby('Borough')['Neighborhood'].count().plot.bar(figsize=(10,5))\nplt.title('Neighborhoods per Borough: Toronto', fontsize = 20)\nplt.xlabel('Borough', fontsize = 15)\nplt.ylabel('No. Neighborhoods',fontsize = 15)\nplt.xticks(rotation = 'horizontal')\nplt.show()",
"execution_count": 40,
"outputs": [
{
"output_type": "display_data",
"data": {
"text/plain": "<Figure size 720x360 with 1 Axes>",
"image/png": "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\n"
},
"metadata": {
"needs_background": "light"
}
}
]
},
{
"metadata": {},
"cell_type": "code",
"source": "CLIENT_ID = 'P3UHP2EBN51LGLSACOIOLSRVSZVCA55NR2XFCVEXUPHGE0BH' # your Foursquare ID\nCLIENT_SECRET = 'PVKHZPMTFH2LFZ1B2P35GSBPBBK1LDSPRRER4EY3LAN4WB45' # your Foursquare Secret\nVERSION = '20180604'",
"execution_count": 21,
"outputs": []
},
{
"metadata": {},
"cell_type": "code",
"source": "def getNearbyVenues(names, latitudes, longitudes):\n radius=500\n LIMIT=100\n venues_list=[]\n for name, lat, lng in zip(names, latitudes, longitudes):\n print(name)\n \n # create the API request URL\n url = 'https://api.foursquare.com/v2/venues/explore?&client_id={}&client_secret={}&v={}&ll={},{}&radius={}&limit={}'.format(\n CLIENT_ID, \n CLIENT_SECRET, \n VERSION, \n lat, \n lng, \n radius, \n LIMIT)\n \n # make the GET request\n results = requests.get(url).json()[\"response\"]['groups'][0]['items']\n \n # return only relevant information for each nearby venue\n venues_list.append([(\n name, \n lat, \n lng, \n v['venue']['name'], \n v['venue']['location']['lat'], \n v['venue']['location']['lng'], \n v['venue']['categories'][0]['name']) for v in results])\n\n nearby_venues = pd.DataFrame([item for venue_list in venues_list for item in venue_list])\n nearby_venues.columns = ['Neighborhood', \n 'Neighborhood Latitude', \n 'Neighborhood Longitude', \n 'Venue', \n 'Venue Latitude', \n 'Venue Longitude', \n 'Venue Category']\n \n return(nearby_venues)",
"execution_count": 22,
"outputs": []
},
{
"metadata": {},
"cell_type": "code",
"source": "toronto_venues = getNearbyVenues(names=toronto_data['Neighborhood'],\n latitudes=toronto_data['Latitude'],\n longitudes=toronto_data['Longitude']\n )",
"execution_count": 23,
"outputs": [
{
"output_type": "stream",
"text": "The Beaches\nThe Danforth West, Riverdale\nIndia Bazaar, The Beaches West\nStudio District\nLawrence Park\nDavisville North\nNorth Toronto West, Lawrence Park\nDavisville\nMoore Park, Summerhill East\nSummerhill West, Rathnelly, South Hill, Forest Hill SE, Deer Park\nRosedale\nSt. James Town, Cabbagetown\nChurch and Wellesley\nRegent Park, Harbourfront\nGarden District, Ryerson\nSt. James Town\nBerczy Park\nCentral Bay Street\nRichmond, Adelaide, King\nHarbourfront East, Union Station, Toronto Islands\nToronto Dominion Centre, Design Exchange\nCommerce Court, Victoria Hotel\nRoselawn\nForest Hill North & West, Forest Hill Road Park\nThe Annex, North Midtown, Yorkville\nUniversity of Toronto, Harbord\nKensington Market, Chinatown, Grange Park\nCN Tower, King and Spadina, Railway Lands, Harbourfront West, Bathurst Quay, South Niagara, Island airport\nStn A PO Boxes\nFirst Canadian Place, Underground city\nChristie\nDufferin, Dovercourt Village\nLittle Portugal, Trinity\nBrockton, Parkdale Village, Exhibition Place\nHigh Park, The Junction South\nParkdale, Roncesvalles\nRunnymede, Swansea\nQueen's Park, Ontario Provincial Government\nBusiness reply mail Processing Centre, South Central Letter Processing Plant Toronto\n",
"name": "stdout"
}
]
},
{
"metadata": {},
"cell_type": "code",
"source": "toronto_venues.head()",
"execution_count": 24,
"outputs": [
{
"output_type": "execute_result",
"execution_count": 24,
"data": {
"text/plain": " Neighborhood Neighborhood Latitude \\\n0 The Beaches 43.676357 \n1 The Beaches 43.676357 \n2 The Beaches 43.676357 \n3 The Beaches 43.676357 \n4 The Danforth West, Riverdale 43.679557 \n\n Neighborhood Longitude Venue Venue Latitude \\\n0 -79.293031 Glen Manor Ravine 43.676821 \n1 -79.293031 The Big Carrot Natural Food Market 43.678879 \n2 -79.293031 Grover Pub and Grub 43.679181 \n3 -79.293031 Upper Beaches 43.680563 \n4 -79.352188 MenEssentials 43.677820 \n\n Venue Longitude Venue Category \n0 -79.293942 Trail \n1 -79.297734 Health Food Store \n2 -79.297215 Pub \n3 -79.292869 Neighborhood \n4 -79.351265 Cosmetics Shop ",
"text/html": "<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>Neighborhood</th>\n <th>Neighborhood Latitude</th>\n <th>Neighborhood Longitude</th>\n <th>Venue</th>\n <th>Venue Latitude</th>\n <th>Venue Longitude</th>\n <th>Venue Category</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <th>0</th>\n <td>The Beaches</td>\n <td>43.676357</td>\n <td>-79.293031</td>\n <td>Glen Manor Ravine</td>\n <td>43.676821</td>\n <td>-79.293942</td>\n <td>Trail</td>\n </tr>\n <tr>\n <th>1</th>\n <td>The Beaches</td>\n <td>43.676357</td>\n <td>-79.293031</td>\n <td>The Big Carrot Natural Food Market</td>\n <td>43.678879</td>\n <td>-79.297734</td>\n <td>Health Food Store</td>\n </tr>\n <tr>\n <th>2</th>\n <td>The Beaches</td>\n <td>43.676357</td>\n <td>-79.293031</td>\n <td>Grover Pub and Grub</td>\n <td>43.679181</td>\n <td>-79.297215</td>\n <td>Pub</td>\n </tr>\n <tr>\n <th>3</th>\n <td>The Beaches</td>\n <td>43.676357</td>\n <td>-79.293031</td>\n <td>Upper Beaches</td>\n <td>43.680563</td>\n <td>-79.292869</td>\n <td>Neighborhood</td>\n </tr>\n <tr>\n <th>4</th>\n <td>The Danforth West, Riverdale</td>\n <td>43.679557</td>\n <td>-79.352188</td>\n <td>MenEssentials</td>\n <td>43.677820</td>\n <td>-79.351265</td>\n <td>Cosmetics Shop</td>\n </tr>\n </tbody>\n</table>\n</div>"
},
"metadata": {}
}
]
},
{
"metadata": {},
"cell_type": "code",
"source": "toronto_venues.groupby('Neighborhood').count()",
"execution_count": 25,
"outputs": [
{
"output_type": "execute_result",
"execution_count": 25,
"data": {
"text/plain": " Neighborhood Latitude \\\nNeighborhood \nBerczy Park 58 \nBrockton, Parkdale Village, Exhibition Place 24 \nBusiness reply mail Processing Centre, South Ce... 17 \nCN Tower, King and Spadina, Railway Lands, Harb... 17 \nCentral Bay Street 65 \nChristie 16 \nChurch and Wellesley 77 \nCommerce Court, Victoria Hotel 100 \nDavisville 35 \nDavisville North 8 \nDufferin, Dovercourt Village 14 \nFirst Canadian Place, Underground city 100 \nForest Hill North & West, Forest Hill Road Park 4 \nGarden District, Ryerson 100 \nHarbourfront East, Union Station, Toronto Islands 100 \nHigh Park, The Junction South 24 \nIndia Bazaar, The Beaches West 24 \nKensington Market, Chinatown, Grange Park 59 \nLawrence Park 3 \nLittle Portugal, Trinity 44 \nMoore Park, Summerhill East 2 \nNorth Toronto West, Lawrence Park 19 \nParkdale, Roncesvalles 15 \nQueen's Park, Ontario Provincial Government 33 \nRegent Park, Harbourfront 45 \nRichmond, Adelaide, King 94 \nRosedale 4 \nRoselawn 2 \nRunnymede, Swansea 37 \nSt. James Town 80 \nSt. James Town, Cabbagetown 44 \nStn A PO Boxes 97 \nStudio District 40 \nSummerhill West, Rathnelly, South Hill, Forest ... 16 \nThe Annex, North Midtown, Yorkville 23 \nThe Beaches 4 \nThe Danforth West, Riverdale 43 \nToronto Dominion Centre, Design Exchange 100 \nUniversity of Toronto, Harbord 35 \n\n Neighborhood Longitude \\\nNeighborhood \nBerczy Park 58 \nBrockton, Parkdale Village, Exhibition Place 24 \nBusiness reply mail Processing Centre, South Ce... 17 \nCN Tower, King and Spadina, Railway Lands, Harb... 17 \nCentral Bay Street 65 \nChristie 16 \nChurch and Wellesley 77 \nCommerce Court, Victoria Hotel 100 \nDavisville 35 \nDavisville North 8 \nDufferin, Dovercourt Village 14 \nFirst Canadian Place, Underground city 100 \nForest Hill North & West, Forest Hill Road Park 4 \nGarden District, Ryerson 100 \nHarbourfront East, Union Station, Toronto Islands 100 \nHigh Park, The Junction South 24 \nIndia Bazaar, The Beaches West 24 \nKensington Market, Chinatown, Grange Park 59 \nLawrence Park 3 \nLittle Portugal, Trinity 44 \nMoore Park, Summerhill East 2 \nNorth Toronto West, Lawrence Park 19 \nParkdale, Roncesvalles 15 \nQueen's Park, Ontario Provincial Government 33 \nRegent Park, Harbourfront 45 \nRichmond, Adelaide, King 94 \nRosedale 4 \nRoselawn 2 \nRunnymede, Swansea 37 \nSt. James Town 80 \nSt. James Town, Cabbagetown 44 \nStn A PO Boxes 97 \nStudio District 40 \nSummerhill West, Rathnelly, South Hill, Forest ... 16 \nThe Annex, North Midtown, Yorkville 23 \nThe Beaches 4 \nThe Danforth West, Riverdale 43 \nToronto Dominion Centre, Design Exchange 100 \nUniversity of Toronto, Harbord 35 \n\n Venue Venue Latitude \\\nNeighborhood \nBerczy Park 58 58 \nBrockton, Parkdale Village, Exhibition Place 24 24 \nBusiness reply mail Processing Centre, South Ce... 17 17 \nCN Tower, King and Spadina, Railway Lands, Harb... 17 17 \nCentral Bay Street 65 65 \nChristie 16 16 \nChurch and Wellesley 77 77 \nCommerce Court, Victoria Hotel 100 100 \nDavisville 35 35 \nDavisville North 8 8 \nDufferin, Dovercourt Village 14 14 \nFirst Canadian Place, Underground city 100 100 \nForest Hill North & West, Forest Hill Road Park 4 4 \nGarden District, Ryerson 100 100 \nHarbourfront East, Union Station, Toronto Islands 100 100 \nHigh Park, The Junction South 24 24 \nIndia Bazaar, The Beaches West 24 24 \nKensington Market, Chinatown, Grange Park 59 59 \nLawrence Park 3 3 \nLittle Portugal, Trinity 44 44 \nMoore Park, Summerhill East 2 2 \nNorth Toronto West, Lawrence Park 19 19 \nParkdale, Roncesvalles 15 15 \nQueen's Park, Ontario Provincial Government 33 33 \nRegent Park, Harbourfront 45 45 \nRichmond, Adelaide, King 94 94 \nRosedale 4 4 \nRoselawn 2 2 \nRunnymede, Swansea 37 37 \nSt. James Town 80 80 \nSt. James Town, Cabbagetown 44 44 \nStn A PO Boxes 97 97 \nStudio District 40 40 \nSummerhill West, Rathnelly, South Hill, Forest ... 16 16 \nThe Annex, North Midtown, Yorkville 23 23 \nThe Beaches 4 4 \nThe Danforth West, Riverdale 43 43 \nToronto Dominion Centre, Design Exchange 100 100 \nUniversity of Toronto, Harbord 35 35 \n\n Venue Longitude \\\nNeighborhood \nBerczy Park 58 \nBrockton, Parkdale Village, Exhibition Place 24 \nBusiness reply mail Processing Centre, South Ce... 17 \nCN Tower, King and Spadina, Railway Lands, Harb... 17 \nCentral Bay Street 65 \nChristie 16 \nChurch and Wellesley 77 \nCommerce Court, Victoria Hotel 100 \nDavisville 35 \nDavisville North 8 \nDufferin, Dovercourt Village 14 \nFirst Canadian Place, Underground city 100 \nForest Hill North & West, Forest Hill Road Park 4 \nGarden District, Ryerson 100 \nHarbourfront East, Union Station, Toronto Islands 100 \nHigh Park, The Junction South 24 \nIndia Bazaar, The Beaches West 24 \nKensington Market, Chinatown, Grange Park 59 \nLawrence Park 3 \nLittle Portugal, Trinity 44 \nMoore Park, Summerhill East 2 \nNorth Toronto West, Lawrence Park 19 \nParkdale, Roncesvalles 15 \nQueen's Park, Ontario Provincial Government 33 \nRegent Park, Harbourfront 45 \nRichmond, Adelaide, King 94 \nRosedale 4 \nRoselawn 2 \nRunnymede, Swansea 37 \nSt. James Town 80 \nSt. James Town, Cabbagetown 44 \nStn A PO Boxes 97 \nStudio District 40 \nSummerhill West, Rathnelly, South Hill, Forest ... 16 \nThe Annex, North Midtown, Yorkville 23 \nThe Beaches 4 \nThe Danforth West, Riverdale 43 \nToronto Dominion Centre, Design Exchange 100 \nUniversity of Toronto, Harbord 35 \n\n Venue Category \nNeighborhood \nBerczy Park 58 \nBrockton, Parkdale Village, Exhibition Place 24 \nBusiness reply mail Processing Centre, South Ce... 17 \nCN Tower, King and Spadina, Railway Lands, Harb... 17 \nCentral Bay Street 65 \nChristie 16 \nChurch and Wellesley 77 \nCommerce Court, Victoria Hotel 100 \nDavisville 35 \nDavisville North 8 \nDufferin, Dovercourt Village 14 \nFirst Canadian Place, Underground city 100 \nForest Hill North & West, Forest Hill Road Park 4 \nGarden District, Ryerson 100 \nHarbourfront East, Union Station, Toronto Islands 100 \nHigh Park, The Junction South 24 \nIndia Bazaar, The Beaches West 24 \nKensington Market, Chinatown, Grange Park 59 \nLawrence Park 3 \nLittle Portugal, Trinity 44 \nMoore Park, Summerhill East 2 \nNorth Toronto West, Lawrence Park 19 \nParkdale, Roncesvalles 15 \nQueen's Park, Ontario Provincial Government 33 \nRegent Park, Harbourfront 45 \nRichmond, Adelaide, King 94 \nRosedale 4 \nRoselawn 2 \nRunnymede, Swansea 37 \nSt. James Town 80 \nSt. James Town, Cabbagetown 44 \nStn A PO Boxes 97 \nStudio District 40 \nSummerhill West, Rathnelly, South Hill, Forest ... 16 \nThe Annex, North Midtown, Yorkville 23 \nThe Beaches 4 \nThe Danforth West, Riverdale 43 \nToronto Dominion Centre, Design Exchange 100 \nUniversity of Toronto, Harbord 35 ",
"text/html": "<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>Neighborhood Latitude</th>\n <th>Neighborhood Longitude</th>\n <th>Venue</th>\n <th>Venue Latitude</th>\n <th>Venue Longitude</th>\n <th>Venue Category</th>\n </tr>\n <tr>\n <th>Neighborhood</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>Berczy Park</th>\n <td>58</td>\n <td>58</td>\n <td>58</td>\n <td>58</td>\n <td>58</td>\n <td>58</td>\n </tr>\n <tr>\n <th>Brockton, Parkdale Village, Exhibition Place</th>\n <td>24</td>\n <td>24</td>\n <td>24</td>\n <td>24</td>\n <td>24</td>\n <td>24</td>\n </tr>\n <tr>\n <th>Business reply mail Processing Centre, South Central Letter Processing Plant Toronto</th>\n <td>17</td>\n <td>17</td>\n <td>17</td>\n <td>17</td>\n <td>17</td>\n <td>17</td>\n </tr>\n <tr>\n <th>CN Tower, King and Spadina, Railway Lands, Harbourfront West, Bathurst Quay, South Niagara, Island airport</th>\n <td>17</td>\n <td>17</td>\n <td>17</td>\n <td>17</td>\n <td>17</td>\n <td>17</td>\n </tr>\n <tr>\n <th>Central Bay Street</th>\n <td>65</td>\n <td>65</td>\n <td>65</td>\n <td>65</td>\n <td>65</td>\n <td>65</td>\n </tr>\n <tr>\n <th>Christie</th>\n <td>16</td>\n <td>16</td>\n <td>16</td>\n <td>16</td>\n <td>16</td>\n <td>16</td>\n </tr>\n <tr>\n <th>Church and Wellesley</th>\n <td>77</td>\n <td>77</td>\n <td>77</td>\n <td>77</td>\n <td>77</td>\n <td>77</td>\n </tr>\n <tr>\n <th>Commerce Court, Victoria Hotel</th>\n <td>100</td>\n <td>100</td>\n <td>100</td>\n <td>100</td>\n <td>100</td>\n <td>100</td>\n </tr>\n <tr>\n <th>Davisville</th>\n <td>35</td>\n <td>35</td>\n <td>35</td>\n <td>35</td>\n <td>35</td>\n <td>35</td>\n </tr>\n <tr>\n <th>Davisville North</th>\n <td>8</td>\n <td>8</td>\n <td>8</td>\n <td>8</td>\n <td>8</td>\n <td>8</td>\n </tr>\n <tr>\n <th>Dufferin, Dovercourt Village</th>\n <td>14</td>\n <td>14</td>\n <td>14</td>\n <td>14</td>\n <td>14</td>\n <td>14</td>\n </tr>\n <tr>\n <th>First Canadian Place, Underground city</th>\n <td>100</td>\n <td>100</td>\n <td>100</td>\n <td>100</td>\n <td>100</td>\n <td>100</td>\n </tr>\n <tr>\n <th>Forest Hill North &amp; West, Forest Hill Road Park</th>\n <td>4</td>\n <td>4</td>\n <td>4</td>\n <td>4</td>\n <td>4</td>\n <td>4</td>\n </tr>\n <tr>\n <th>Garden District, Ryerson</th>\n <td>100</td>\n <td>100</td>\n <td>100</td>\n <td>100</td>\n <td>100</td>\n <td>100</td>\n </tr>\n <tr>\n <th>Harbourfront East, Union Station, Toronto Islands</th>\n <td>100</td>\n <td>100</td>\n <td>100</td>\n <td>100</td>\n <td>100</td>\n <td>100</td>\n </tr>\n <tr>\n <th>High Park, The Junction South</th>\n <td>24</td>\n <td>24</td>\n <td>24</td>\n <td>24</td>\n <td>24</td>\n <td>24</td>\n </tr>\n <tr>\n <th>India Bazaar, The Beaches West</th>\n <td>24</td>\n <td>24</td>\n <td>24</td>\n <td>24</td>\n <td>24</td>\n <td>24</td>\n </tr>\n <tr>\n <th>Kensington Market, Chinatown, Grange Park</th>\n <td>59</td>\n <td>59</td>\n <td>59</td>\n <td>59</td>\n <td>59</td>\n <td>59</td>\n </tr>\n <tr>\n <th>Lawrence Park</th>\n <td>3</td>\n <td>3</td>\n <td>3</td>\n <td>3</td>\n <td>3</td>\n <td>3</td>\n </tr>\n <tr>\n <th>Little Portugal, Trinity</th>\n <td>44</td>\n <td>44</td>\n <td>44</td>\n <td>44</td>\n <td>44</td>\n <td>44</td>\n </tr>\n <tr>\n <th>Moore Park, Summerhill East</th>\n <td>2</td>\n <td>2</td>\n <td>2</td>\n <td>2</td>\n <td>2</td>\n <td>2</td>\n </tr>\n <tr>\n <th>North Toronto West, Lawrence Park</th>\n <td>19</td>\n <td>19</td>\n <td>19</td>\n <td>19</td>\n <td>19</td>\n <td>19</td>\n </tr>\n <tr>\n <th>Parkdale, Roncesvalles</th>\n <td>15</td>\n <td>15</td>\n <td>15</td>\n <td>15</td>\n <td>15</td>\n <td>15</td>\n </tr>\n <tr>\n <th>Queen's Park, Ontario Provincial Government</th>\n <td>33</td>\n <td>33</td>\n <td>33</td>\n <td>33</td>\n <td>33</td>\n <td>33</td>\n </tr>\n <tr>\n <th>Regent Park, Harbourfront</th>\n <td>45</td>\n <td>45</td>\n <td>45</td>\n <td>45</td>\n <td>45</td>\n <td>45</td>\n </tr>\n <tr>\n <th>Richmond, Adelaide, King</th>\n <td>94</td>\n <td>94</td>\n <td>94</td>\n <td>94</td>\n <td>94</td>\n <td>94</td>\n </tr>\n <tr>\n <th>Rosedale</th>\n <td>4</td>\n <td>4</td>\n <td>4</td>\n <td>4</td>\n <td>4</td>\n <td>4</td>\n </tr>\n <tr>\n <th>Roselawn</th>\n <td>2</td>\n <td>2</td>\n <td>2</td>\n <td>2</td>\n <td>2</td>\n <td>2</td>\n </tr>\n <tr>\n <th>Runnymede, Swansea</th>\n <td>37</td>\n <td>37</td>\n <td>37</td>\n <td>37</td>\n <td>37</td>\n <td>37</td>\n </tr>\n <tr>\n <th>St. James Town</th>\n <td>80</td>\n <td>80</td>\n <td>80</td>\n <td>80</td>\n <td>80</td>\n <td>80</td>\n </tr>\n <tr>\n <th>St. James Town, Cabbagetown</th>\n <td>44</td>\n <td>44</td>\n <td>44</td>\n <td>44</td>\n <td>44</td>\n <td>44</td>\n </tr>\n <tr>\n <th>Stn A PO Boxes</th>\n <td>97</td>\n <td>97</td>\n <td>97</td>\n <td>97</td>\n <td>97</td>\n <td>97</td>\n </tr>\n <tr>\n <th>Studio District</th>\n <td>40</td>\n <td>40</td>\n <td>40</td>\n <td>40</td>\n <td>40</td>\n <td>40</td>\n </tr>\n <tr>\n <th>Summerhill West, Rathnelly, South Hill, Forest Hill SE, Deer Park</th>\n <td>16</td>\n <td>16</td>\n <td>16</td>\n <td>16</td>\n <td>16</td>\n <td>16</td>\n </tr>\n <tr>\n <th>The Annex, North Midtown, Yorkville</th>\n <td>23</td>\n <td>23</td>\n <td>23</td>\n <td>23</td>\n <td>23</td>\n <td>23</td>\n </tr>\n <tr>\n <th>The Beaches</th>\n <td>4</td>\n <td>4</td>\n <td>4</td>\n <td>4</td>\n <td>4</td>\n <td>4</td>\n </tr>\n <tr>\n <th>The Danforth West, Riverdale</th>\n <td>43</td>\n <td>43</td>\n <td>43</td>\n <td>43</td>\n <td>43</td>\n <td>43</td>\n </tr>\n <tr>\n <th>Toronto Dominion Centre, Design Exchange</th>\n <td>100</td>\n <td>100</td>\n <td>100</td>\n <td>100</td>\n <td>100</td>\n <td>100</td>\n </tr>\n <tr>\n <th>University of Toronto, Harbord</th>\n <td>35</td>\n <td>35</td>\n <td>35</td>\n <td>35</td>\n <td>35</td>\n <td>35</td>\n </tr>\n </tbody>\n</table>\n</div>"
},
"metadata": {}
}
]
},
{
"metadata": {},
"cell_type": "code",
"source": "toronto_onehot = pd.get_dummies(toronto_venues[['Venue Category']], prefix=\"\", prefix_sep=\"\")\ntoronto_onehot.drop(['Neighborhood'],axis=1,inplace=True) \ntoronto_onehot.insert(loc=0, column='Neighborhood', value=toronto_venues['Neighborhood'] )\ntoronto_onehot.shape",
"execution_count": 26,
"outputs": [
{
"output_type": "execute_result",
"execution_count": 26,
"data": {
"text/plain": "(1622, 233)"
},
"metadata": {}
}
]
},
{
"metadata": {},
"cell_type": "code",
"source": "toronto_grouped = toronto_onehot.groupby('Neighborhood').mean().reset_index()\ntoronto_grouped.head()",
"execution_count": 27,
"outputs": [
{
"output_type": "execute_result",
"execution_count": 27,
"data": {
"text/plain": " Neighborhood Afghan Restaurant \\\n0 Berczy Park 0.0 \n1 Brockton, Parkdale Village, Exhibition Place 0.0 \n2 Business reply mail Processing Centre, South C... 0.0 \n3 CN Tower, King and Spadina, Railway Lands, Har... 0.0 \n4 Central Bay Street 0.0 \n\n Airport Airport Food Court Airport Gate Airport Lounge \\\n0 0.000000 0.000000 0.000000 0.000000 \n1 0.000000 0.000000 0.000000 0.000000 \n2 0.000000 0.000000 0.000000 0.000000 \n3 0.058824 0.058824 0.058824 0.117647 \n4 0.000000 0.000000 0.000000 0.000000 \n\n Airport Service Airport Terminal American Restaurant Antique Shop ... \\\n0 0.000000 0.000000 0.0 0.0 ... \n1 0.000000 0.000000 0.0 0.0 ... \n2 0.000000 0.000000 0.0 0.0 ... \n3 0.176471 0.117647 0.0 0.0 ... \n4 0.000000 0.000000 0.0 0.0 ... \n\n Toy / Game Store Trail Train Station Vegetarian / Vegan Restaurant \\\n0 0.0 0.0 0.0 0.017241 \n1 0.0 0.0 0.0 0.000000 \n2 0.0 0.0 0.0 0.000000 \n3 0.0 0.0 0.0 0.000000 \n4 0.0 0.0 0.0 0.015385 \n\n Video Game Store Vietnamese Restaurant Wine Bar Wine Shop \\\n0 0.0 0.0 0.000000 0.0 \n1 0.0 0.0 0.000000 0.0 \n2 0.0 0.0 0.000000 0.0 \n3 0.0 0.0 0.000000 0.0 \n4 0.0 0.0 0.015385 0.0 \n\n Women's Store Yoga Studio \n0 0.0 0.000000 \n1 0.0 0.000000 \n2 0.0 0.058824 \n3 0.0 0.000000 \n4 0.0 0.015385 \n\n[5 rows x 233 columns]",
"text/html": "<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>Neighborhood</th>\n <th>Afghan Restaurant</th>\n <th>Airport</th>\n <th>Airport Food Court</th>\n <th>Airport Gate</th>\n <th>Airport Lounge</th>\n <th>Airport Service</th>\n <th>Airport Terminal</th>\n <th>American Restaurant</th>\n <th>Antique Shop</th>\n <th>...</th>\n <th>Toy / Game Store</th>\n <th>Trail</th>\n <th>Train Station</th>\n <th>Vegetarian / Vegan Restaurant</th>\n <th>Video Game Store</th>\n <th>Vietnamese Restaurant</th>\n <th>Wine Bar</th>\n <th>Wine Shop</th>\n <th>Women's Store</th>\n <th>Yoga Studio</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <th>0</th>\n <td>Berczy Park</td>\n <td>0.0</td>\n <td>0.000000</td>\n <td>0.000000</td>\n <td>0.000000</td>\n <td>0.000000</td>\n <td>0.000000</td>\n <td>0.000000</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>...</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>0.017241</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>0.000000</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>0.000000</td>\n </tr>\n <tr>\n <th>1</th>\n <td>Brockton, Parkdale Village, Exhibition Place</td>\n <td>0.0</td>\n <td>0.000000</td>\n <td>0.000000</td>\n <td>0.000000</td>\n <td>0.000000</td>\n <td>0.000000</td>\n <td>0.000000</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>...</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>0.000000</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>0.000000</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>0.000000</td>\n </tr>\n <tr>\n <th>2</th>\n <td>Business reply mail Processing Centre, South C...</td>\n <td>0.0</td>\n <td>0.000000</td>\n <td>0.000000</td>\n <td>0.000000</td>\n <td>0.000000</td>\n <td>0.000000</td>\n <td>0.000000</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>...</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>0.000000</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>0.000000</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>0.058824</td>\n </tr>\n <tr>\n <th>3</th>\n <td>CN Tower, King and Spadina, Railway Lands, Har...</td>\n <td>0.0</td>\n <td>0.058824</td>\n <td>0.058824</td>\n <td>0.058824</td>\n <td>0.117647</td>\n <td>0.176471</td>\n <td>0.117647</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>...</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>0.000000</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>0.000000</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>0.000000</td>\n </tr>\n <tr>\n <th>4</th>\n <td>Central Bay Street</td>\n <td>0.0</td>\n <td>0.000000</td>\n <td>0.000000</td>\n <td>0.000000</td>\n <td>0.000000</td>\n <td>0.000000</td>\n <td>0.000000</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>...</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>0.015385</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>0.015385</td>\n <td>0.0</td>\n <td>0.0</td>\n <td>0.015385</td>\n </tr>\n </tbody>\n</table>\n<p>5 rows \u00d7 233 columns</p>\n</div>"
},
"metadata": {}
}
]
},
{
"metadata": {},
"cell_type": "code",
"source": "def return_most_common_venues(row, num_top_venues):\n row_categories = row.iloc[1:]\n row_categories_sorted = row_categories.sort_values(ascending=False)\n \n return row_categories_sorted.index.values[0:num_top_venues]",
"execution_count": 31,
"outputs": []
},
{
"metadata": {},
"cell_type": "code",
"source": "num_top_venues = 10\n\nindicators = ['st', 'nd', 'rd']\n\n# create columns according to number of top venues\ncolumns = ['Neighborhood']\nfor ind in np.arange(num_top_venues):\n try:\n columns.append('{}{} Most Common Venue'.format(ind+1, indicators[ind]))\n except:\n columns.append('{}th Most Common Venue'.format(ind+1))\n\n# create a new dataframe\nneighborhoods_venues_sorted = pd.DataFrame(columns=columns)\nneighborhoods_venues_sorted['Neighborhood'] = toronto_grouped['Neighborhood']\n\nfor ind in np.arange(toronto_grouped.shape[0]):\n neighborhoods_venues_sorted.iloc[ind, 1:] = return_most_common_venues(toronto_grouped.iloc[ind, :], num_top_venues)\n\nneighborhoods_venues_sorted.head()",
"execution_count": 32,
"outputs": [
{
"output_type": "execute_result",
"execution_count": 32,
"data": {
"text/plain": " Neighborhood 1st Most Common Venue \\\n0 Berczy Park Coffee Shop \n1 Brockton, Parkdale Village, Exhibition Place Caf\u00e9 \n2 Business reply mail Processing Centre, South C... Yoga Studio \n3 CN Tower, King and Spadina, Railway Lands, Har... Airport Service \n4 Central Bay Street Coffee Shop \n\n 2nd Most Common Venue 3rd Most Common Venue 4th Most Common Venue \\\n0 Cocktail Bar Seafood Restaurant Bakery \n1 Bakery Breakfast Spot Coffee Shop \n2 Auto Workshop Burrito Place Light Rail Station \n3 Airport Lounge Airport Terminal Boutique \n4 Italian Restaurant Sandwich Place Japanese Restaurant \n\n 5th Most Common Venue 6th Most Common Venue 7th Most Common Venue \\\n0 Restaurant Caf\u00e9 Beer Bar \n1 Gym Stadium Burrito Place \n2 Farmers Market Fast Food Restaurant Butcher \n3 Coffee Shop Airport Airport Food Court \n4 Caf\u00e9 Thai Restaurant Salad Place \n\n 8th Most Common Venue 9th Most Common Venue 10th Most Common Venue \n0 Cheese Shop Department Store Japanese Restaurant \n1 Restaurant Climbing Gym Performing Arts Venue \n2 Restaurant Recording Studio Brewery \n3 Airport Gate Sculpture Garden Rental Car Location \n4 Bar Burger Joint Bubble Tea Shop ",
"text/html": "<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>Neighborhood</th>\n <th>1st Most Common Venue</th>\n <th>2nd Most Common Venue</th>\n <th>3rd Most Common Venue</th>\n <th>4th Most Common Venue</th>\n <th>5th Most Common Venue</th>\n <th>6th Most Common Venue</th>\n <th>7th Most Common Venue</th>\n <th>8th Most Common Venue</th>\n <th>9th Most Common Venue</th>\n <th>10th Most Common Venue</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <th>0</th>\n <td>Berczy Park</td>\n <td>Coffee Shop</td>\n <td>Cocktail Bar</td>\n <td>Seafood Restaurant</td>\n <td>Bakery</td>\n <td>Restaurant</td>\n <td>Caf\u00e9</td>\n <td>Beer Bar</td>\n <td>Cheese Shop</td>\n <td>Department Store</td>\n <td>Japanese Restaurant</td>\n </tr>\n <tr>\n <th>1</th>\n <td>Brockton, Parkdale Village, Exhibition Place</td>\n <td>Caf\u00e9</td>\n <td>Bakery</td>\n <td>Breakfast Spot</td>\n <td>Coffee Shop</td>\n <td>Gym</td>\n <td>Stadium</td>\n <td>Burrito Place</td>\n <td>Restaurant</td>\n <td>Climbing Gym</td>\n <td>Performing Arts Venue</td>\n </tr>\n <tr>\n <th>2</th>\n <td>Business reply mail Processing Centre, South C...</td>\n <td>Yoga Studio</td>\n <td>Auto Workshop</td>\n <td>Burrito Place</td>\n <td>Light Rail Station</td>\n <td>Farmers Market</td>\n <td>Fast Food Restaurant</td>\n <td>Butcher</td>\n <td>Restaurant</td>\n <td>Recording Studio</td>\n <td>Brewery</td>\n </tr>\n <tr>\n <th>3</th>\n <td>CN Tower, King and Spadina, Railway Lands, Har...</td>\n <td>Airport Service</td>\n <td>Airport Lounge</td>\n <td>Airport Terminal</td>\n <td>Boutique</td>\n <td>Coffee Shop</td>\n <td>Airport</td>\n <td>Airport Food Court</td>\n <td>Airport Gate</td>\n <td>Sculpture Garden</td>\n <td>Rental Car Location</td>\n </tr>\n <tr>\n <th>4</th>\n <td>Central Bay Street</td>\n <td>Coffee Shop</td>\n <td>Italian Restaurant</td>\n <td>Sandwich Place</td>\n <td>Japanese Restaurant</td>\n <td>Caf\u00e9</td>\n <td>Thai Restaurant</td>\n <td>Salad Place</td>\n <td>Bar</td>\n <td>Burger Joint</td>\n <td>Bubble Tea Shop</td>\n </tr>\n </tbody>\n</table>\n</div>"
},
"metadata": {}
}
]
},
{
"metadata": {},
"cell_type": "code",
"source": "# set number of clusters\nkclusters = 5\n\ntoronto_grouped_clustering = toronto_grouped.drop('Neighborhood', 1)\n\n# run k-means clustering\nkmeans = KMeans(n_clusters=kclusters, random_state=0).fit(toronto_grouped_clustering)\n\n# check cluster labels generated for each row in the dataframe\nkmeans.labels_[0:10]",
"execution_count": 33,
"outputs": [
{
"output_type": "execute_result",
"execution_count": 33,
"data": {
"text/plain": "array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0], dtype=int32)"
},
"metadata": {}
}
]
},
{
"metadata": {},
"cell_type": "code",
"source": "# add clustering labels\nneighborhoods_venues_sorted.insert(0, 'Cluster Labels', kmeans.labels_)\n\ntoronto_merged = toronto_data\n\n# merge toronto_grouped with toronto_data to add latitude/longitude for each neighborhood\ntoronto_merged = toronto_merged.join(neighborhoods_venues_sorted.set_index('Neighborhood'), on='Neighborhood')\n\ntoronto_merged.head()\n",
"execution_count": 34,
"outputs": [
{
"output_type": "execute_result",
"execution_count": 34,
"data": {
"text/plain": " Postalcode Borough Neighborhood Latitude \\\n37 M4E East Toronto The Beaches 43.676357 \n41 M4K East Toronto The Danforth West, Riverdale 43.679557 \n42 M4L East Toronto India Bazaar, The Beaches West 43.668999 \n43 M4M East Toronto Studio District 43.659526 \n44 M4N Central Toronto Lawrence Park 43.728020 \n\n Longitude Cluster Labels 1st Most Common Venue 2nd Most Common Venue \\\n37 -79.293031 0 Health Food Store Trail \n41 -79.352188 0 Greek Restaurant Italian Restaurant \n42 -79.315572 0 Park Sandwich Place \n43 -79.340923 0 Caf\u00e9 Coffee Shop \n44 -79.388790 4 Park Swim School \n\n 3rd Most Common Venue 4th Most Common Venue 5th Most Common Venue \\\n37 Pub Yoga Studio Deli / Bodega \n41 Coffee Shop Bookstore Restaurant \n42 Fast Food Restaurant Pizza Place Gym \n43 Bakery Gastropub American Restaurant \n44 Bus Line Yoga Studio Dessert Shop \n\n 6th Most Common Venue 7th Most Common Venue 8th Most Common Venue \\\n37 Ethiopian Restaurant Electronics Store Eastern European Restaurant \n41 Ice Cream Shop Furniture / Home Store Yoga Studio \n42 Brewery Burrito Place Restaurant \n43 Brewery Yoga Studio Fish Market \n44 Event Space Ethiopian Restaurant Electronics Store \n\n 9th Most Common Venue 10th Most Common Venue \n37 Donut Shop Doner Restaurant \n41 Liquor Store Spa \n42 Pub Pet Store \n43 Italian Restaurant Bookstore \n44 Eastern European Restaurant Donut Shop ",
"text/html": "<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>Postalcode</th>\n <th>Borough</th>\n <th>Neighborhood</th>\n <th>Latitude</th>\n <th>Longitude</th>\n <th>Cluster Labels</th>\n <th>1st Most Common Venue</th>\n <th>2nd Most Common Venue</th>\n <th>3rd Most Common Venue</th>\n <th>4th Most Common Venue</th>\n <th>5th Most Common Venue</th>\n <th>6th Most Common Venue</th>\n <th>7th Most Common Venue</th>\n <th>8th Most Common Venue</th>\n <th>9th Most Common Venue</th>\n <th>10th Most Common Venue</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <th>37</th>\n <td>M4E</td>\n <td>East Toronto</td>\n <td>The Beaches</td>\n <td>43.676357</td>\n <td>-79.293031</td>\n <td>0</td>\n <td>Health Food Store</td>\n <td>Trail</td>\n <td>Pub</td>\n <td>Yoga Studio</td>\n <td>Deli / Bodega</td>\n <td>Ethiopian Restaurant</td>\n <td>Electronics Store</td>\n <td>Eastern European Restaurant</td>\n <td>Donut Shop</td>\n <td>Doner Restaurant</td>\n </tr>\n <tr>\n <th>41</th>\n <td>M4K</td>\n <td>East Toronto</td>\n <td>The Danforth West, Riverdale</td>\n <td>43.679557</td>\n <td>-79.352188</td>\n <td>0</td>\n <td>Greek Restaurant</td>\n <td>Italian Restaurant</td>\n <td>Coffee Shop</td>\n <td>Bookstore</td>\n <td>Restaurant</td>\n <td>Ice Cream Shop</td>\n <td>Furniture / Home Store</td>\n <td>Yoga Studio</td>\n <td>Liquor Store</td>\n <td>Spa</td>\n </tr>\n <tr>\n <th>42</th>\n <td>M4L</td>\n <td>East Toronto</td>\n <td>India Bazaar, The Beaches West</td>\n <td>43.668999</td>\n <td>-79.315572</td>\n <td>0</td>\n <td>Park</td>\n <td>Sandwich Place</td>\n <td>Fast Food Restaurant</td>\n <td>Pizza Place</td>\n <td>Gym</td>\n <td>Brewery</td>\n <td>Burrito Place</td>\n <td>Restaurant</td>\n <td>Pub</td>\n <td>Pet Store</td>\n </tr>\n <tr>\n <th>43</th>\n <td>M4M</td>\n <td>East Toronto</td>\n <td>Studio District</td>\n <td>43.659526</td>\n <td>-79.340923</td>\n <td>0</td>\n <td>Caf\u00e9</td>\n <td>Coffee Shop</td>\n <td>Bakery</td>\n <td>Gastropub</td>\n <td>American Restaurant</td>\n <td>Brewery</td>\n <td>Yoga Studio</td>\n <td>Fish Market</td>\n <td>Italian Restaurant</td>\n <td>Bookstore</td>\n </tr>\n <tr>\n <th>44</th>\n <td>M4N</td>\n <td>Central Toronto</td>\n <td>Lawrence Park</td>\n <td>43.728020</td>\n <td>-79.388790</td>\n <td>4</td>\n <td>Park</td>\n <td>Swim School</td>\n <td>Bus Line</td>\n <td>Yoga Studio</td>\n <td>Dessert Shop</td>\n <td>Event Space</td>\n <td>Ethiopian Restaurant</td>\n <td>Electronics Store</td>\n <td>Eastern European Restaurant</td>\n <td>Donut Shop</td>\n </tr>\n </tbody>\n</table>\n</div>"
},
"metadata": {}
}
]
},
{
"metadata": {},
"cell_type": "code",
"source": "neighborhoods_venues_sorted.head()",
"execution_count": 35,
"outputs": [
{
"output_type": "execute_result",
"execution_count": 35,
"data": {
"text/plain": " Cluster Labels Neighborhood \\\n0 0 Berczy Park \n1 0 Brockton, Parkdale Village, Exhibition Place \n2 0 Business reply mail Processing Centre, South C... \n3 0 CN Tower, King and Spadina, Railway Lands, Har... \n4 0 Central Bay Street \n\n 1st Most Common Venue 2nd Most Common Venue 3rd Most Common Venue \\\n0 Coffee Shop Cocktail Bar Seafood Restaurant \n1 Caf\u00e9 Bakery Breakfast Spot \n2 Yoga Studio Auto Workshop Burrito Place \n3 Airport Service Airport Lounge Airport Terminal \n4 Coffee Shop Italian Restaurant Sandwich Place \n\n 4th Most Common Venue 5th Most Common Venue 6th Most Common Venue \\\n0 Bakery Restaurant Caf\u00e9 \n1 Coffee Shop Gym Stadium \n2 Light Rail Station Farmers Market Fast Food Restaurant \n3 Boutique Coffee Shop Airport \n4 Japanese Restaurant Caf\u00e9 Thai Restaurant \n\n 7th Most Common Venue 8th Most Common Venue 9th Most Common Venue \\\n0 Beer Bar Cheese Shop Department Store \n1 Burrito Place Restaurant Climbing Gym \n2 Butcher Restaurant Recording Studio \n3 Airport Food Court Airport Gate Sculpture Garden \n4 Salad Place Bar Burger Joint \n\n 10th Most Common Venue \n0 Japanese Restaurant \n1 Performing Arts Venue \n2 Brewery \n3 Rental Car Location \n4 Bubble Tea Shop ",
"text/html": "<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>Cluster Labels</th>\n <th>Neighborhood</th>\n <th>1st Most Common Venue</th>\n <th>2nd Most Common Venue</th>\n <th>3rd Most Common Venue</th>\n <th>4th Most Common Venue</th>\n <th>5th Most Common Venue</th>\n <th>6th Most Common Venue</th>\n <th>7th Most Common Venue</th>\n <th>8th Most Common Venue</th>\n <th>9th Most Common Venue</th>\n <th>10th Most Common Venue</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <th>0</th>\n <td>0</td>\n <td>Berczy Park</td>\n <td>Coffee Shop</td>\n <td>Cocktail Bar</td>\n <td>Seafood Restaurant</td>\n <td>Bakery</td>\n <td>Restaurant</td>\n <td>Caf\u00e9</td>\n <td>Beer Bar</td>\n <td>Cheese Shop</td>\n <td>Department Store</td>\n <td>Japanese Restaurant</td>\n </tr>\n <tr>\n <th>1</th>\n <td>0</td>\n <td>Brockton, Parkdale Village, Exhibition Place</td>\n <td>Caf\u00e9</td>\n <td>Bakery</td>\n <td>Breakfast Spot</td>\n <td>Coffee Shop</td>\n <td>Gym</td>\n <td>Stadium</td>\n <td>Burrito Place</td>\n <td>Restaurant</td>\n <td>Climbing Gym</td>\n <td>Performing Arts Venue</td>\n </tr>\n <tr>\n <th>2</th>\n <td>0</td>\n <td>Business reply mail Processing Centre, South C...</td>\n <td>Yoga Studio</td>\n <td>Auto Workshop</td>\n <td>Burrito Place</td>\n <td>Light Rail Station</td>\n <td>Farmers Market</td>\n <td>Fast Food Restaurant</td>\n <td>Butcher</td>\n <td>Restaurant</td>\n <td>Recording Studio</td>\n <td>Brewery</td>\n </tr>\n <tr>\n <th>3</th>\n <td>0</td>\n <td>CN Tower, King and Spadina, Railway Lands, Har...</td>\n <td>Airport Service</td>\n <td>Airport Lounge</td>\n <td>Airport Terminal</td>\n <td>Boutique</td>\n <td>Coffee Shop</td>\n <td>Airport</td>\n <td>Airport Food Court</td>\n <td>Airport Gate</td>\n <td>Sculpture Garden</td>\n <td>Rental Car Location</td>\n </tr>\n <tr>\n <th>4</th>\n <td>0</td>\n <td>Central Bay Street</td>\n <td>Coffee Shop</td>\n <td>Italian Restaurant</td>\n <td>Sandwich Place</td>\n <td>Japanese Restaurant</td>\n <td>Caf\u00e9</td>\n <td>Thai Restaurant</td>\n <td>Salad Place</td>\n <td>Bar</td>\n <td>Burger Joint</td>\n <td>Bubble Tea Shop</td>\n </tr>\n </tbody>\n</table>\n</div>"
},
"metadata": {}
}
]
},
{
"metadata": {},
"cell_type": "code",
"source": "address = 'Toronto, CA'\n\ngeolocator = Nominatim(user_agent=\"ny_explorer\")\nlocation = geolocator.geocode(address)\nlatitude = location.latitude\nlongitude = location.longitude\nprint('The geograpical coordinate of Manhattan are {}, {}.'.format(latitude, longitude))",
"execution_count": 36,
"outputs": [
{
"output_type": "stream",
"text": "The geograpical coordinate of Manhattan are 43.6534817, -79.3839347.\n",
"name": "stdout"
}
]
},
{
"metadata": {},
"cell_type": "code",
"source": "! pip install folium",
"execution_count": 37,
"outputs": [
{
"output_type": "stream",
"text": "Collecting folium\n\u001b[?25l Downloading https://files.pythonhosted.org/packages/a4/f0/44e69d50519880287cc41e7c8a6acc58daa9a9acf5f6afc52bcc70f69a6d/folium-0.11.0-py2.py3-none-any.whl (93kB)\n\u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588| 102kB 8.3MB/s ta 0:00:011\n\u001b[?25hCollecting branca>=0.3.0 (from folium)\n Downloading https://files.pythonhosted.org/packages/13/fb/9eacc24ba3216510c6b59a4ea1cd53d87f25ba76237d7f4393abeaf4c94e/branca-0.4.1-py3-none-any.whl\nRequirement already satisfied: requests in /opt/conda/envs/Python36/lib/python3.6/site-packages (from folium) (2.21.0)\nRequirement already satisfied: numpy in /opt/conda/envs/Python36/lib/python3.6/site-packages (from folium) (1.15.4)\nRequirement already satisfied: jinja2>=2.9 in /opt/conda/envs/Python36/lib/python3.6/site-packages (from folium) (2.10)\nRequirement already satisfied: certifi>=2017.4.17 in /opt/conda/envs/Python36/lib/python3.6/site-packages (from requests->folium) (2020.6.20)\nRequirement already satisfied: chardet<3.1.0,>=3.0.2 in /opt/conda/envs/Python36/lib/python3.6/site-packages (from requests->folium) (3.0.4)\nRequirement already satisfied: idna<2.9,>=2.5 in /opt/conda/envs/Python36/lib/python3.6/site-packages (from requests->folium) (2.8)\nRequirement already satisfied: urllib3<1.25,>=1.21.1 in /opt/conda/envs/Python36/lib/python3.6/site-packages (from requests->folium) (1.24.1)\nRequirement already satisfied: MarkupSafe>=0.23 in /opt/conda/envs/Python36/lib/python3.6/site-packages (from jinja2>=2.9->folium) (1.1.0)\nInstalling collected packages: branca, folium\nSuccessfully installed branca-0.4.1 folium-0.11.0\n",
"name": "stdout"
}
]
},
{
"metadata": {},
"cell_type": "code",
"source": "import folium",
"execution_count": 38,
"outputs": []
},
{
"metadata": {},
"cell_type": "code",
"source": "map_clusters = folium.Map(location=[latitude, longitude], zoom_start=11)\n\n# set color scheme for the clusters\nx = np.arange(kclusters)\nys = [i + x + (i*x)**2 for i in range(kclusters)]\ncolors_array = cm.rainbow(np.linspace(0, 1, len(ys)))\nrainbow = [colors.rgb2hex(i) for i in colors_array]\n\n# add markers to the map\nmarkers_colors = []\nfor lat, lon, poi, cluster in zip(toronto_merged['Latitude'], toronto_merged['Longitude'], toronto_merged['Neighborhood'], toronto_merged['Cluster Labels']):\n label = folium.Popup(str(poi) + ' Cluster ' + str(cluster), parse_html=True)\n folium.CircleMarker(\n [lat, lon],\n radius=5,\n popup=label,\n color=rainbow[cluster-1],\n fill=True,\n fill_color=rainbow[cluster-1],\n fill_opacity=0.7).add_to(map_clusters)\n \nmap_clusters",
"execution_count": 39,
"outputs": [
{
"output_type": "execute_result",
"execution_count": 39,
"data": {
"text/plain": "<folium.folium.Map at 0x7fc44a118c88>",
"text/html": "<div style=\"width:100%;\"><div style=\"position:relative;width:100%;height:0;padding-bottom:60%;\"><span style=\"color:#565656\">Make this Notebook Trusted to load map: File -> Trust Notebook</span><iframe src=\"about:blank\" style=\"position:absolute;width:100%;height:100%;left:0;top:0;border:none !important;\" data-html=PCFET0NUWVBFIGh0bWw+CjxoZWFkPiAgICAKICAgIDxtZXRhIGh0dHAtZXF1aXY9ImNvbnRlbnQtdHlwZSIgY29udGVudD0idGV4dC9odG1sOyBjaGFyc2V0PVVURi04IiAvPgogICAgCiAgICAgICAgPHNjcmlwdD4KICAgICAgICAgICAgTF9OT19UT1VDSCA9IGZhbHNlOwogICAgICAgICAgICBMX0RJU0FCTEVfM0QgPSBmYWxzZTsKICAgICAgICA8L3NjcmlwdD4KICAgIAogICAgPHNjcmlwdCBzcmM9Imh0dHBzOi8vY2RuLmpzZGVsaXZyLm5ldC9ucG0vbGVhZmxldEAxLjYuMC9kaXN0L2xlYWZsZXQuanMiPjwvc2NyaXB0PgogICAgPHNjcmlwdCBzcmM9Imh0dHBzOi8vY29kZS5qcXVlcnkuY29tL2pxdWVyeS0xLjEyLjQubWluLmpzIj48L3NjcmlwdD4KICAgIDxzY3JpcHQgc3JjPSJodHRwczovL21heGNkbi5ib290c3RyYXBjZG4uY29tL2Jvb3RzdHJhcC8zLjIuMC9qcy9ib290c3RyYXAubWluLmpzIj48L3NjcmlwdD4KICAgIDxzY3JpcHQgc3JjPSJodHRwczovL2NkbmpzLmNsb3VkZmxhcmUuY29tL2FqYXgvbGlicy9MZWFmbGV0LmF3ZXNvbWUtbWFya2Vycy8yLjAuMi9sZWFmbGV0LmF3ZXNvbWUtbWFya2Vycy5qcyI+PC9zY3JpcHQ+CiAgICA8bGluayByZWw9InN0eWxlc2hlZXQiIGhyZWY9Imh0dHBzOi8vY2RuLmpzZGVsaXZyLm5ldC9ucG0vbGVhZmxldEAxLjYuMC9kaXN0L2xlYWZsZXQuY3NzIi8+CiAgICA8bGluayByZWw9InN0eWxlc2hlZXQiIGhyZWY9Imh0dHBzOi8vbWF4Y2RuLmJvb3RzdHJhcGNkbi5jb20vYm9vdHN0cmFwLzMuMi4wL2Nzcy9ib290c3RyYXAubWluLmNzcyIvPgogICAgPGxpbmsgcmVsPSJzdHlsZXNoZWV0IiBocmVmPSJodHRwczovL21heGNkbi5ib290c3RyYXBjZG4uY29tL2Jvb3RzdHJhcC8zLjIuMC9jc3MvYm9vdHN0cmFwLXRoZW1lLm1pbi5jc3MiLz4KICAgIDxsaW5rIHJlbD0ic3R5bGVzaGVldCIgaHJlZj0iaHR0cHM6Ly9tYXhjZG4uYm9vdHN0cmFwY2RuLmNvbS9mb250LWF3ZXNvbWUvNC42LjMvY3NzL2ZvbnQtYXdlc29tZS5taW4uY3NzIi8+CiAgICA8bGluayByZWw9InN0eWxlc2hlZXQiIGhyZWY9Imh0dHBzOi8vY2RuanMuY2xvdWRmbGFyZS5jb20vYWpheC9saWJzL0xlYWZsZXQuYXdlc29tZS1tYXJrZXJzLzIuMC4yL2xlYWZsZXQuYXdlc29tZS1tYXJrZXJzLmNzcyIvPgogICAgPGxpbmsgcmVsPSJzdHlsZXNoZWV0IiBocmVmPSJodHRwczovL3Jhd2Nkbi5naXRoYWNrLmNvbS9weXRob24tdmlzdWFsaXphdGlvbi9mb2xpdW0vbWFzdGVyL2ZvbGl1bS90ZW1wbGF0ZXMvbGVhZmxldC5hd2Vzb21lLnJvdGF0ZS5jc3MiLz4KICAgIDxzdHlsZT5odG1sLCBib2R5IHt3aWR0aDogMTAwJTtoZWlnaHQ6IDEwMCU7bWFyZ2luOiAwO3BhZGRpbmc6IDA7fTwvc3R5bGU+CiAgICA8c3R5bGU+I21hcCB7cG9zaXRpb246YWJzb2x1dGU7dG9wOjA7Ym90dG9tOjA7cmlnaHQ6MDtsZWZ0OjA7fTwvc3R5bGU+CiAgICAKICAgICAgICAgICAgPG1ldGEgbmFtZT0idmlld3BvcnQiIGNvbnRlbnQ9IndpZHRoPWRldmljZS13aWR0aCwKICAgICAgICAgICAgICAgIGluaXRpYWwtc2NhbGU9MS4wLCBtYXhpbXVtLXNjYWxlPTEuMCwgdXNlci1zY2FsYWJsZT1ubyIgLz4KICAgICAgICAgICAgPHN0eWxlPgogICAgICAgICAgICAgICAgI21hcF81MjNlMDVhMmY2ZTE0NTE2YTcyNDVjNmM5MTNlZmIwOSB7CiAgICAgICAgICAgICAgICAgICAgcG9zaXRpb246IHJlbGF0aXZlOwogICAgICAgICAgICAgICAgICAgIHdpZHRoOiAxMDAuMCU7CiAgICAgICAgICAgICAgICAgICAgaGVpZ2h0OiAxMDAuMCU7CiAgICAgICAgICAgICAgICAgICAgbGVmdDogMC4wJTsKICAgICAgICAgICAgICAgICAgICB0b3A6IDAuMCU7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIDwvc3R5bGU+CiAgICAgICAgCjwvaGVhZD4KPGJvZHk+ICAgIAogICAgCiAgICAgICAgICAgIDxkaXYgY2xhc3M9ImZvbGl1bS1tYXAiIGlkPSJtYXBfNTIzZTA1YTJmNmUxNDUxNmE3MjQ1YzZjOTEzZWZiMDkiID48L2Rpdj4KICAgICAgICAKPC9ib2R5Pgo8c2NyaXB0PiAgICAKICAgIAogICAgICAgICAgICB2YXIgbWFwXzUyM2UwNWEyZjZlMTQ1MTZhNzI0NWM2YzkxM2VmYjA5ID0gTC5tYXAoCiAgICAgICAgICAgICAgICAibWFwXzUyM2UwNWEyZjZlMTQ1MTZhNzI0NWM2YzkxM2VmYjA5IiwKICAgICAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICAgICBjZW50ZXI6IFs0My42NTM0ODE3LCAtNzkuMzgzOTM0N10sCiAgICAgICAgICAgICAgICAgICAgY3JzOiBMLkNSUy5FUFNHMzg1NywKICAgICAgICAgICAgICAgICAgICB6b29tOiAxMSwKICAgICAgICAgICAgICAgICAgICB6b29tQ29udHJvbDogdHJ1ZSwKICAgICAgICAgICAgICAgICAgICBwcmVmZXJDYW52YXM6IGZhbHNlLAogICAgICAgICAgICAgICAgfQogICAgICAgICAgICApOwoKICAgICAgICAgICAgCgogICAgICAgIAogICAgCiAgICAgICAgICAgIHZhciB0aWxlX2xheWVyX2VkZjcxNTgzZTkzYzQ4ODVhNGVhY2UwZDQ2OTk1MjA1ID0gTC50aWxlTGF5ZXIoCiAgICAgICAgICAgICAgICAiaHR0cHM6Ly97c30udGlsZS5vcGVuc3RyZWV0bWFwLm9yZy97en0ve3h9L3t5fS5wbmciLAogICAgICAgICAgICAgICAgeyJhdHRyaWJ1dGlvbiI6ICJEYXRhIGJ5IFx1MDAyNmNvcHk7IFx1MDAzY2EgaHJlZj1cImh0dHA6Ly9vcGVuc3RyZWV0bWFwLm9yZ1wiXHUwMDNlT3BlblN0cmVldE1hcFx1MDAzYy9hXHUwMDNlLCB1bmRlciBcdTAwM2NhIGhyZWY9XCJodHRwOi8vd3d3Lm9wZW5zdHJlZXRtYXAub3JnL2NvcHlyaWdodFwiXHUwMDNlT0RiTFx1MDAzYy9hXHUwMDNlLiIsICJkZXRlY3RSZXRpbmEiOiBmYWxzZSwgIm1heE5hdGl2ZVpvb20iOiAxOCwgIm1heFpvb20iOiAxOCwgIm1pblpvb20iOiAwLCAibm9XcmFwIjogZmFsc2UsICJvcGFjaXR5IjogMSwgInN1YmRvbWFpbnMiOiAiYWJjIiwgInRtcyI6IGZhbHNlfQogICAgICAgICAgICApLmFkZFRvKG1hcF81MjNlMDVhMmY2ZTE0NTE2YTcyNDVjNmM5MTNlZmIwOSk7CiAgICAgICAgCiAgICAKICAgICAgICAgICAgdmFyIGNpcmNsZV9tYXJrZXJfNjBiNDVhMGUwZDdhNDk1OWFlOGY5YWIyOTJkMzY3OGIgPSBMLmNpcmNsZU1hcmtlcigKICAgICAgICAgICAgICAgIFs0My42NzYzNTczOTk5OTk5OSwgLTc5LjI5MzAzMTJdLAogICAgICAgICAgICAgICAgeyJidWJibGluZ01vdXNlRXZlbnRzIjogdHJ1ZSwgImNvbG9yIjogIiNmZjAwMDAiLCAiZGFzaEFycmF5IjogbnVsbCwgImRhc2hPZmZzZXQiOiBudWxsLCAiZmlsbCI6IHRydWUsICJmaWxsQ29sb3IiOiAiI2ZmMDAwMCIsICJmaWxsT3BhY2l0eSI6IDAuNywgImZpbGxSdWxlIjogImV2ZW5vZGQiLCAibGluZUNhcCI6ICJyb3VuZCIsICJsaW5lSm9pbiI6ICJyb3VuZCIsICJvcGFjaXR5IjogMS4wLCAicmFkaXVzIjogNSwgInN0cm9rZSI6IHRydWUsICJ3ZWlnaHQiOiAzfQogICAgICAgICAgICApLmFkZFRvKG1hcF81MjNlMDVhMmY2ZTE0NTE2YTcyNDVjNmM5MTNlZmIwOSk7CiAgICAgICAgCiAgICAKICAgICAgICB2YXIgcG9wdXBfNjJmNWQ4ZjViZThiNDk1MTllZTkzZWM0NmZlOTNjNmQgPSBMLnBvcHVwKHsibWF4V2lkdGgiOiAiMTAwJSJ9KTsKCiAgICAgICAgCiAgICAgICAgICAgIHZhciBodG1sX2JhNmY4ZDRkZGYzNzQ2MzQ4ZTkzOGYxZjNjZGUwODlmID0gJChgPGRpdiBpZD0iaHRtbF9iYTZmOGQ0ZGRmMzc0NjM0OGU5MzhmMWYzY2RlMDg5ZiIgc3R5bGU9IndpZHRoOiAxMDAuMCU7IGhlaWdodDogMTAwLjAlOyI+VGhlIEJlYWNoZXMgQ2x1c3RlciAwPC9kaXY+YClbMF07CiAgICAgICAgICAgIHBvcHVwXzYyZjVkOGY1YmU4YjQ5NTE5ZWU5M2VjNDZmZTkzYzZkLnNldENvbnRlbnQoaHRtbF9iYTZmOGQ0ZGRmMzc0NjM0OGU5MzhmMWYzY2RlMDg5Zik7CiAgICAgICAgCgogICAgICAgIGNpcmNsZV9tYXJrZXJfNjBiNDVhMGUwZDdhNDk1OWFlOGY5YWIyOTJkMzY3OGIuYmluZFBvcHVwKHBvcHVwXzYyZjVkOGY1YmU4YjQ5NTE5ZWU5M2VjNDZmZTkzYzZkKQogICAgICAgIDsKCiAgICAgICAgCiAgICAKICAgIAogICAgICAgICAgICB2YXIgY2lyY2xlX21hcmtlcl9hMDRjYzhmZmI3MWY0ODNhODRhNzNkMTE0MzYyNWZlMSA9IEwuY2lyY2xlTWFya2VyKAogICAgICAgICAgICAgICAgWzQzLjY3OTU1NzEsIC03OS4zNTIxODhdLAogICAgICAgICAgICAgICAgeyJidWJibGluZ01vdXNlRXZlbnRzIjogdHJ1ZSwgImNvbG9yIjogIiNmZjAwMDAiLCAiZGFzaEFycmF5IjogbnVsbCwgImRhc2hPZmZzZXQiOiBudWxsLCAiZmlsbCI6IHRydWUsICJmaWxsQ29sb3IiOiAiI2ZmMDAwMCIsICJmaWxsT3BhY2l0eSI6IDAuNywgImZpbGxSdWxlIjogImV2ZW5vZGQiLCAibGluZUNhcCI6ICJyb3VuZCIsICJsaW5lSm9pbiI6ICJyb3VuZCIsICJvcGFjaXR5IjogMS4wLCAicmFkaXVzIjogNSwgInN0cm9rZSI6IHRydWUsICJ3ZWlnaHQiOiAzfQogICAgICAgICAgICApLmFkZFRvKG1hcF81MjNlMDVhMmY2ZTE0NTE2YTcyNDVjNmM5MTNlZmIwOSk7CiAgICAgICAgCiAgICAKICAgICAgICB2YXIgcG9wdXBfMjJkYmRmNDViMzdjNGMwOWFhN2E4ZjJjYzI3OWU4NTcgPSBMLnBvcHVwKHsibWF4V2lkdGgiOiAiMTAwJSJ9KTsKCiAgICAgICAgCiAgICAgICAgICAgIHZhciBodG1sX2ViNmQ5ZTgwZjY5ZjQ3MmFiODJlYmI2ZGU1M2M1MGFjID0gJChgPGRpdiBpZD0iaHRtbF9lYjZkOWU4MGY2OWY0NzJhYjgyZWJiNmRlNTNjNTBhYyIgc3R5bGU9IndpZHRoOiAxMDAuMCU7IGhlaWdodDogMTAwLjAlOyI+VGhlIERhbmZvcnRoIFdlc3QsIFJpdmVyZGFsZSBDbHVzdGVyIDA8L2Rpdj5gKVswXTsKICAgICAgICAgICAgcG9wdXBfMjJkYmRmNDViMzdjNGMwOWFhN2E4ZjJjYzI3OWU4NTcuc2V0Q29udGVudChodG1sX2ViNmQ5ZTgwZjY5ZjQ3MmFiODJlYmI2ZGU1M2M1MGFjKTsKICAgICAgICAKCiAgICAgICAgY2lyY2xlX21hcmtlcl9hMDRjYzhmZmI3MWY0ODNhODRhNzNkMTE0MzYyNWZlMS5iaW5kUG9wdXAocG9wdXBfMjJkYmRmNDViMzdjNGMwOWFhN2E4ZjJjYzI3OWU4NTcpCiAgICAgICAgOwoKICAgICAgICAKICAgIAogICAgCiAgICAgICAgICAgIHZhciBjaXJjbGVfbWFya2VyX2Q3NTk3ZDcxOTUwMzQ2OTQ5MDcxZTkxNjQ2YWNhNTYwID0gTC5jaXJjbGVNYXJrZXIoCiAgICAgICAgICAgICAgICBbNDMuNjY4OTk4NSwgLTc5LjMxNTU3MTU5OTk5OTk4XSwKICAgICAgICAgICAgICAgIHsiYnViYmxpbmdNb3VzZUV2ZW50cyI6IHRydWUsICJjb2xvciI6ICIjZmYwMDAwIiwgImRhc2hBcnJheSI6IG51bGwsICJkYXNoT2Zmc2V0IjogbnVsbCwgImZpbGwiOiB0cnVlLCAiZmlsbENvbG9yIjogIiNmZjAwMDAiLCAiZmlsbE9wYWNpdHkiOiAwLjcsICJmaWxsUnVsZSI6ICJldmVub2RkIiwgImxpbmVDYXAiOiAicm91bmQiLCAibGluZUpvaW4iOiAicm91bmQiLCAib3BhY2l0eSI6IDEuMCwgInJhZGl1cyI6IDUsICJzdHJva2UiOiB0cnVlLCAid2VpZ2h0IjogM30KICAgICAgICAgICAgKS5hZGRUbyhtYXBfNTIzZTA1YTJmNmUxNDUxNmE3MjQ1YzZjOTEzZWZiMDkpOwogICAgICAgIAogICAgCiAgICAgICAgdmFyIHBvcHVwX2QwMmI5OTg4OGZiMDRmOGNiZmUyNzY2YzJlNmFkMWI4ID0gTC5wb3B1cCh7Im1heFdpZHRoIjogIjEwMCUifSk7CgogICAgICAgIAogICAgICAgICAgICB2YXIgaHRtbF83NmZkZGNhODY4OTM0YTZhYTQ3NDA1NjBhMjk0MDcwZSA9ICQoYDxkaXYgaWQ9Imh0bWxfNzZmZGRjYTg2ODkzNGE2YWE0NzQwNTYwYTI5NDA3MGUiIHN0eWxlPSJ3aWR0aDogMTAwLjAlOyBoZWlnaHQ6IDEwMC4wJTsiPkluZGlhIEJhemFhciwgVGhlIEJlYWNoZXMgV2VzdCBDbHVzdGVyIDA8L2Rpdj5gKVswXTsKICAgICAgICAgICAgcG9wdXBfZDAyYjk5ODg4ZmIwNGY4Y2JmZTI3NjZjMmU2YWQxYjguc2V0Q29udGVudChodG1sXzc2ZmRkY2E4Njg5MzRhNmFhNDc0MDU2MGEyOTQwNzBlKTsKICAgICAgICAKCiAgICAgICAgY2lyY2xlX21hcmtlcl9kNzU5N2Q3MTk1MDM0Njk0OTA3MWU5MTY0NmFjYTU2MC5iaW5kUG9wdXAocG9wdXBfZDAyYjk5ODg4ZmIwNGY4Y2JmZTI3NjZjMmU2YWQxYjgpCiAgICAgICAgOwoKICAgICAgICAKICAgIAogICAgCiAgICAgICAgICAgIHZhciBjaXJjbGVfbWFya2VyXzAzODQ5ZWU0ZDVhNDRmYzVhMjFmZjcwMDZiMGM1ZDVmID0gTC5jaXJjbGVNYXJrZXIoCiAgICAgICAgICAgICAgICBbNDMuNjU5NTI1NSwgLTc5LjM0MDkyM10sCiAgICAgICAgICAgICAgICB7ImJ1YmJsaW5nTW91c2VFdmVudHMiOiB0cnVlLCAiY29sb3IiOiAiI2ZmMDAwMCIsICJkYXNoQXJyYXkiOiBudWxsLCAiZGFzaE9mZnNldCI6IG51bGwsICJmaWxsIjogdHJ1ZSwgImZpbGxDb2xvciI6ICIjZmYwMDAwIiwgImZpbGxPcGFjaXR5IjogMC43LCAiZmlsbFJ1bGUiOiAiZXZlbm9kZCIsICJsaW5lQ2FwIjogInJvdW5kIiwgImxpbmVKb2luIjogInJvdW5kIiwgIm9wYWNpdHkiOiAxLjAsICJyYWRpdXMiOiA1LCAic3Ryb2tlIjogdHJ1ZSwgIndlaWdodCI6IDN9CiAgICAgICAgICAgICkuYWRkVG8obWFwXzUyM2UwNWEyZjZlMTQ1MTZhNzI0NWM2YzkxM2VmYjA5KTsKICAgICAgICAKICAgIAogICAgICAgIHZhciBwb3B1cF9mZjFjMWY3ZDQyY2E0Yjc5YTFhYTA4YjZlODY5YzRiNCA9IEwucG9wdXAoeyJtYXhXaWR0aCI6ICIxMDAlIn0pOwoKICAgICAgICAKICAgICAgICAgICAgdmFyIGh0bWxfMDExMjNhYzQ3OGI4NDk5OGJmZDRmYmYzN2U5YTkxMWEgPSAkKGA8ZGl2IGlkPSJodG1sXzAxMTIzYWM0NzhiODQ5OThiZmQ0ZmJmMzdlOWE5MTFhIiBzdHlsZT0id2lkdGg6IDEwMC4wJTsgaGVpZ2h0OiAxMDAuMCU7Ij5TdHVkaW8gRGlzdHJpY3QgQ2x1c3RlciAwPC9kaXY+YClbMF07CiAgICAgICAgICAgIHBvcHVwX2ZmMWMxZjdkNDJjYTRiNzlhMWFhMDhiNmU4NjljNGI0LnNldENvbnRlbnQoaHRtbF8wMTEyM2FjNDc4Yjg0OTk4YmZkNGZiZjM3ZTlhOTExYSk7CiAgICAgICAgCgogICAgICAgIGNpcmNsZV9tYXJrZXJfMDM4NDllZTRkNWE0NGZjNWEyMWZmNzAwNmIwYzVkNWYuYmluZFBvcHVwKHBvcHVwX2ZmMWMxZjdkNDJjYTRiNzlhMWFhMDhiNmU4NjljNGI0KQogICAgICAgIDsKCiAgICAgICAgCiAgICAKICAgIAogICAgICAgICAgICB2YXIgY2lyY2xlX21hcmtlcl9mZTUxZTZiNjdhNTQ0OTRiYmIyOWRmNDI0MWIwNmZmMiA9IEwuY2lyY2xlTWFya2VyKAogICAgICAgICAgICAgICAgWzQzLjcyODAyMDUsIC03OS4zODg3OTAxXSwKICAgICAgICAgICAgICAgIHsiYnViYmxpbmdNb3VzZUV2ZW50cyI6IHRydWUsICJjb2xvciI6ICIjZmZiMzYwIiwgImRhc2hBcnJheSI6IG51bGwsICJkYXNoT2Zmc2V0IjogbnVsbCwgImZpbGwiOiB0cnVlLCAiZmlsbENvbG9yIjogIiNmZmIzNjAiLCAiZmlsbE9wYWNpdHkiOiAwLjcsICJmaWxsUnVsZSI6ICJldmVub2RkIiwgImxpbmVDYXAiOiAicm91bmQiLCAibGluZUpvaW4iOiAicm91bmQiLCAib3BhY2l0eSI6IDEuMCwgInJhZGl1cyI6IDUsICJzdHJva2UiOiB0cnVlLCAid2VpZ2h0IjogM30KICAgICAgICAgICAgKS5hZGRUbyhtYXBfNTIzZTA1YTJmNmUxNDUxNmE3MjQ1YzZjOTEzZWZiMDkpOwogICAgICAgIAogICAgCiAgICAgICAgdmFyIHBvcHVwX2UyN2ViNTAyM2JkMTQ1YzRhMTA2NmUxY2RhODQ1NjlmID0gTC5wb3B1cCh7Im1heFdpZHRoIjogIjEwMCUifSk7CgogICAgICAgIAogICAgICAgICAgICB2YXIgaHRtbF80MDgyY2EzNTc1NDE0NDkwYTY2ZDg1MjgzODg2ZTRjNyA9ICQoYDxkaXYgaWQ9Imh0bWxfNDA4MmNhMzU3NTQxNDQ5MGE2NmQ4NTI4Mzg4NmU0YzciIHN0eWxlPSJ3aWR0aDogMTAwLjAlOyBoZWlnaHQ6IDEwMC4wJTsiPkxhd3JlbmNlIFBhcmsgQ2x1c3RlciA0PC9kaXY+YClbMF07CiAgICAgICAgICAgIHBvcHVwX2UyN2ViNTAyM2JkMTQ1YzRhMTA2NmUxY2RhODQ1NjlmLnNldENvbnRlbnQoaHRtbF80MDgyY2EzNTc1NDE0NDkwYTY2ZDg1MjgzODg2ZTRjNyk7CiAgICAgICAgCgogICAgICAgIGNpcmNsZV9tYXJrZXJfZmU1MWU2YjY3YTU0NDk0YmJiMjlkZjQyNDFiMDZmZjIuYmluZFBvcHVwKHBvcHVwX2UyN2ViNTAyM2JkMTQ1YzRhMTA2NmUxY2RhODQ1NjlmKQogICAgICAgIDsKCiAgICAgICAgCiAgICAKICAgIAogICAgICAgICAgICB2YXIgY2lyY2xlX21hcmtlcl9hNDMwYWUzNmJkMTM0ZTExYTA5MTlmMjQ4YWJlMTFjNyA9IEwuY2lyY2xlTWFya2VyKAogICAgICAgICAgICAgICAgWzQzLjcxMjc1MTEsIC03OS4zOTAxOTc1XSwKICAgICAgICAgICAgICAgIHsiYnViYmxpbmdNb3VzZUV2ZW50cyI6IHRydWUsICJjb2xvciI6ICIjZmYwMDAwIiwgImRhc2hBcnJheSI6IG51bGwsICJkYXNoT2Zmc2V0IjogbnVsbCwgImZpbGwiOiB0cnVlLCAiZmlsbENvbG9yIjogIiNmZjAwMDAiLCAiZmlsbE9wYWNpdHkiOiAwLjcsICJmaWxsUnVsZSI6ICJldmVub2RkIiwgImxpbmVDYXAiOiAicm91bmQiLCAibGluZUpvaW4iOiAicm91bmQiLCAib3BhY2l0eSI6IDEuMCwgInJhZGl1cyI6IDUsICJzdHJva2UiOiB0cnVlLCAid2VpZ2h0IjogM30KICAgICAgICAgICAgKS5hZGRUbyhtYXBfNTIzZTA1YTJmNmUxNDUxNmE3MjQ1YzZjOTEzZWZiMDkpOwogICAgICAgIAogICAgCiAgICAgICAgdmFyIHBvcHVwXzRjYzMzZmI5Y2Q0ZjQ0MmZiYTM0MDlkMWQzNTcyMzdhID0gTC5wb3B1cCh7Im1heFdpZHRoIjogIjEwMCUifSk7CgogICAgICAgIAogICAgICAgICAgICB2YXIgaHRtbF82NjQ3NDAyM2M5ZjI0ZGU2YTI4Zjk1MzljZDgyODdhMSA9ICQoYDxkaXYgaWQ9Imh0bWxfNjY0NzQwMjNjOWYyNGRlNmEyOGY5NTM5Y2Q4Mjg3YTEiIHN0eWxlPSJ3aWR0aDogMTAwLjAlOyBoZWlnaHQ6IDEwMC4wJTsiPkRhdmlzdmlsbGUgTm9ydGggQ2x1c3RlciAwPC9kaXY+YClbMF07CiAgICAgICAgICAgIHBvcHVwXzRjYzMzZmI5Y2Q0ZjQ0MmZiYTM0MDlkMWQzNTcyMzdhLnNldENvbnRlbnQoaHRtbF82NjQ3NDAyM2M5ZjI0ZGU2YTI4Zjk1MzljZDgyODdhMSk7CiAgICAgICAgCgogICAgICAgIGNpcmNsZV9tYXJrZXJfYTQzMGFlMzZiZDEzNGUxMWEwOTE5ZjI0OGFiZTExYzcuYmluZFBvcHVwKHBvcHVwXzRjYzMzZmI5Y2Q0ZjQ0MmZiYTM0MDlkMWQzNTcyMzdhKQogICAgICAgIDsKCiAgICAgICAgCiAgICAKICAgIAogICAgICAgICAgICB2YXIgY2lyY2xlX21hcmtlcl81YjJiZjI4ZWVlOGI0ZTkyYjI4ODAwNWY5MTg2MmI4NSA9IEwuY2lyY2xlTWFya2VyKAogICAgICAgICAgICAgICAgWzQzLjcxNTM4MzQsIC03OS40MDU2Nzg0MDAwMDAwMV0sCiAgICAgICAgICAgICAgICB7ImJ1YmJsaW5nTW91c2VFdmVudHMiOiB0cnVlLCAiY29sb3IiOiAiI2ZmMDAwMCIsICJkYXNoQXJyYXkiOiBudWxsLCAiZGFzaE9mZnNldCI6IG51bGwsICJmaWxsIjogdHJ1ZSwgImZpbGxDb2xvciI6ICIjZmYwMDAwIiwgImZpbGxPcGFjaXR5IjogMC43LCAiZmlsbFJ1bGUiOiAiZXZlbm9kZCIsICJsaW5lQ2FwIjogInJvdW5kIiwgImxpbmVKb2luIjogInJvdW5kIiwgIm9wYWNpdHkiOiAxLjAsICJyYWRpdXMiOiA1LCAic3Ryb2tlIjogdHJ1ZSwgIndlaWdodCI6IDN9CiAgICAgICAgICAgICkuYWRkVG8obWFwXzUyM2UwNWEyZjZlMTQ1MTZhNzI0NWM2YzkxM2VmYjA5KTsKICAgICAgICAKICAgIAogICAgICAgIHZhciBwb3B1cF83ODI3ZDZiNWM5ZjA0N2ZlYjEzYWJlNjJhMmFmZTA0YyA9IEwucG9wdXAoeyJtYXhXaWR0aCI6ICIxMDAlIn0pOwoKICAgICAgICAKICAgICAgICAgICAgdmFyIGh0bWxfYzBhMzRkZGQ3MDNlNGJiYmE4MWJhMzI2YjRhZmMyMjggPSAkKGA8ZGl2IGlkPSJodG1sX2MwYTM0ZGRkNzAzZTRiYmJhODFiYTMyNmI0YWZjMjI4IiBzdHlsZT0id2lkdGg6IDEwMC4wJTsgaGVpZ2h0OiAxMDAuMCU7Ij5Ob3J0aCBUb3JvbnRvIFdlc3QsICBMYXdyZW5jZSBQYXJrIENsdXN0ZXIgMDwvZGl2PmApWzBdOwogICAgICAgICAgICBwb3B1cF83ODI3ZDZiNWM5ZjA0N2ZlYjEzYWJlNjJhMmFmZTA0Yy5zZXRDb250ZW50KGh0bWxfYzBhMzRkZGQ3MDNlNGJiYmE4MWJhMzI2YjRhZmMyMjgpOwogICAgICAgIAoKICAgICAgICBjaXJjbGVfbWFya2VyXzViMmJmMjhlZWU4YjRlOTJiMjg4MDA1ZjkxODYyYjg1LmJpbmRQb3B1cChwb3B1cF83ODI3ZDZiNWM5ZjA0N2ZlYjEzYWJlNjJhMmFmZTA0YykKICAgICAgICA7CgogICAgICAgIAogICAgCiAgICAKICAgICAgICAgICAgdmFyIGNpcmNsZV9tYXJrZXJfNDMzMjU1NDUyMWNkNDYxYjlkNjUxZGNhZjYxMTU5MGYgPSBMLmNpcmNsZU1hcmtlcigKICAgICAgICAgICAgICAgIFs0My43MDQzMjQ0LCAtNzkuMzg4NzkwMV0sCiAgICAgICAgICAgICAgICB7ImJ1YmJsaW5nTW91c2VFdmVudHMiOiB0cnVlLCAiY29sb3IiOiAiI2ZmMDAwMCIsICJkYXNoQXJyYXkiOiBudWxsLCAiZGFzaE9mZnNldCI6IG51bGwsICJmaWxsIjogdHJ1ZSwgImZpbGxDb2xvciI6ICIjZmYwMDAwIiwgImZpbGxPcGFjaXR5IjogMC43LCAiZmlsbFJ1bGUiOiAiZXZlbm9kZCIsICJsaW5lQ2FwIjogInJvdW5kIiwgImxpbmVKb2luIjogInJvdW5kIiwgIm9wYWNpdHkiOiAxLjAsICJyYWRpdXMiOiA1LCAic3Ryb2tlIjogdHJ1ZSwgIndlaWdodCI6IDN9CiAgICAgICAgICAgICkuYWRkVG8obWFwXzUyM2UwNWEyZjZlMTQ1MTZhNzI0NWM2YzkxM2VmYjA5KTsKICAgICAgICAKICAgIAogICAgICAgIHZhciBwb3B1cF83ZGM3NGNkYTg1NWY0NDM3YmNmZWZiOGUxNWVlMGFjZCA9IEwucG9wdXAoeyJtYXhXaWR0aCI6ICIxMDAlIn0pOwoKICAgICAgICAKICAgICAgICAgICAgdmFyIGh0bWxfMTliNTc0NDkwOGQwNDRhYWFkZTg2NjFjNzA0M2FlNWIgPSAkKGA8ZGl2IGlkPSJodG1sXzE5YjU3NDQ5MDhkMDQ0YWFhZGU4NjYxYzcwNDNhZTViIiBzdHlsZT0id2lkdGg6IDEwMC4wJTsgaGVpZ2h0OiAxMDAuMCU7Ij5EYXZpc3ZpbGxlIENsdXN0ZXIgMDwvZGl2PmApWzBdOwogICAgICAgICAgICBwb3B1cF83ZGM3NGNkYTg1NWY0NDM3YmNmZWZiOGUxNWVlMGFjZC5zZXRDb250ZW50KGh0bWxfMTliNTc0NDkwOGQwNDRhYWFkZTg2NjFjNzA0M2FlNWIpOwogICAgICAgIAoKICAgICAgICBjaXJjbGVfbWFya2VyXzQzMzI1NTQ1MjFjZDQ2MWI5ZDY1MWRjYWY2MTE1OTBmLmJpbmRQb3B1cChwb3B1cF83ZGM3NGNkYTg1NWY0NDM3YmNmZWZiOGUxNWVlMGFjZCkKICAgICAgICA7CgogICAgICAgIAogICAgCiAgICAKICAgICAgICAgICAgdmFyIGNpcmNsZV9tYXJrZXJfOWU4M2M1NjA2MTRmNGUzMmI5NjVmZmRiMTFhOTViNDIgPSBMLmNpcmNsZU1hcmtlcigKICAgICAgICAgICAgICAgIFs0My42ODk1NzQzLCAtNzkuMzgzMTU5OTAwMDAwMDFdLAogICAgICAgICAgICAgICAgeyJidWJibGluZ01vdXNlRXZlbnRzIjogdHJ1ZSwgImNvbG9yIjogIiM4MDAwZmYiLCAiZGFzaEFycmF5IjogbnVsbCwgImRhc2hPZmZzZXQiOiBudWxsLCAiZmlsbCI6IHRydWUsICJmaWxsQ29sb3IiOiAiIzgwMDBmZiIsICJmaWxsT3BhY2l0eSI6IDAuNywgImZpbGxSdWxlIjogImV2ZW5vZGQiLCAibGluZUNhcCI6ICJyb3VuZCIsICJsaW5lSm9pbiI6ICJyb3VuZCIsICJvcGFjaXR5IjogMS4wLCAicmFkaXVzIjogNSwgInN0cm9rZSI6IHRydWUsICJ3ZWlnaHQiOiAzfQogICAgICAgICAgICApLmFkZFRvKG1hcF81MjNlMDVhMmY2ZTE0NTE2YTcyNDVjNmM5MTNlZmIwOSk7CiAgICAgICAgCiAgICAKICAgICAgICB2YXIgcG9wdXBfMTRiN2M5ZGRiMDBkNDFhNmIzOGNjN2VhYTNhYmQ2MDggPSBMLnBvcHVwKHsibWF4V2lkdGgiOiAiMTAwJSJ9KTsKCiAgICAgICAgCiAgICAgICAgICAgIHZhciBodG1sXzlkYjZmZTdhYzk1MDRlNDRhNDA1MDRjZTIxMzU2NmU3ID0gJChgPGRpdiBpZD0iaHRtbF85ZGI2ZmU3YWM5NTA0ZTQ0YTQwNTA0Y2UyMTM1NjZlNyIgc3R5bGU9IndpZHRoOiAxMDAuMCU7IGhlaWdodDogMTAwLjAlOyI+TW9vcmUgUGFyaywgU3VtbWVyaGlsbCBFYXN0IENsdXN0ZXIgMTwvZGl2PmApWzBdOwogICAgICAgICAgICBwb3B1cF8xNGI3YzlkZGIwMGQ0MWE2YjM4Y2M3ZWFhM2FiZDYwOC5zZXRDb250ZW50KGh0bWxfOWRiNmZlN2FjOTUwNGU0NGE0MDUwNGNlMjEzNTY2ZTcpOwogICAgICAgIAoKICAgICAgICBjaXJjbGVfbWFya2VyXzllODNjNTYwNjE0ZjRlMzJiOTY1ZmZkYjExYTk1YjQyLmJpbmRQb3B1cChwb3B1cF8xNGI3YzlkZGIwMGQ0MWE2YjM4Y2M3ZWFhM2FiZDYwOCkKICAgICAgICA7CgogICAgICAgIAogICAgCiAgICAKICAgICAgICAgICAgdmFyIGNpcmNsZV9tYXJrZXJfOTY5NzgyMjQwNjI1NDFhNjhmNmM0NGM0M2UxZWFhODUgPSBMLmNpcmNsZU1hcmtlcigKICAgICAgICAgICAgICAgIFs0My42ODY0MTIyOTk5OTk5OSwgLTc5LjQwMDA0OTNdLAogICAgICAgICAgICAgICAgeyJidWJibGluZ01vdXNlRXZlbnRzIjogdHJ1ZSwgImNvbG9yIjogIiNmZjAwMDAiLCAiZGFzaEFycmF5IjogbnVsbCwgImRhc2hPZmZzZXQiOiBudWxsLCAiZmlsbCI6IHRydWUsICJmaWxsQ29sb3IiOiAiI2ZmMDAwMCIsICJmaWxsT3BhY2l0eSI6IDAuNywgImZpbGxSdWxlIjogImV2ZW5vZGQiLCAibGluZUNhcCI6ICJyb3VuZCIsICJsaW5lSm9pbiI6ICJyb3VuZCIsICJvcGFjaXR5IjogMS4wLCAicmFkaXVzIjogNSwgInN0cm9rZSI6IHRydWUsICJ3ZWlnaHQiOiAzfQogICAgICAgICAgICApLmFkZFRvKG1hcF81MjNlMDVhMmY2ZTE0NTE2YTcyNDVjNmM5MTNlZmIwOSk7CiAgICAgICAgCiAgICAKICAgICAgICB2YXIgcG9wdXBfNjliZTVjZTZmMzAwNGVmNDllOWVlNDZhNzU1NWE2ODAgPSBMLnBvcHVwKHsibWF4V2lkdGgiOiAiMTAwJSJ9KTsKCiAgICAgICAgCiAgICAgICAgICAgIHZhciBodG1sXzRhMzBkYzgwMmYxYjQwYWNiZTYzN2I5MTQ5ZDQwYjY3ID0gJChgPGRpdiBpZD0iaHRtbF80YTMwZGM4MDJmMWI0MGFjYmU2MzdiOTE0OWQ0MGI2NyIgc3R5bGU9IndpZHRoOiAxMDAuMCU7IGhlaWdodDogMTAwLjAlOyI+U3VtbWVyaGlsbCBXZXN0LCBSYXRobmVsbHksIFNvdXRoIEhpbGwsIEZvcmVzdCBIaWxsIFNFLCBEZWVyIFBhcmsgQ2x1c3RlciAwPC9kaXY+YClbMF07CiAgICAgICAgICAgIHBvcHVwXzY5YmU1Y2U2ZjMwMDRlZjQ5ZTllZTQ2YTc1NTVhNjgwLnNldENvbnRlbnQoaHRtbF80YTMwZGM4MDJmMWI0MGFjYmU2MzdiOTE0OWQ0MGI2Nyk7CiAgICAgICAgCgogICAgICAgIGNpcmNsZV9tYXJrZXJfOTY5NzgyMjQwNjI1NDFhNjhmNmM0NGM0M2UxZWFhODUuYmluZFBvcHVwKHBvcHVwXzY5YmU1Y2U2ZjMwMDRlZjQ5ZTllZTQ2YTc1NTVhNjgwKQogICAgICAgIDsKCiAgICAgICAgCiAgICAKICAgIAogICAgICAgICAgICB2YXIgY2lyY2xlX21hcmtlcl9iODU4Yzc2NzVkZjU0MzM3YTJkMGJhNWM4Zjc3MzExYyA9IEwuY2lyY2xlTWFya2VyKAogICAgICAgICAgICAgICAgWzQzLjY3OTU2MjYsIC03OS4zNzc1Mjk0MDAwMDAwMV0sCiAgICAgICAgICAgICAgICB7ImJ1YmJsaW5nTW91c2VFdmVudHMiOiB0cnVlLCAiY29sb3IiOiAiIzAwYjVlYiIsICJkYXNoQXJyYXkiOiBudWxsLCAiZGFzaE9mZnNldCI6IG51bGwsICJmaWxsIjogdHJ1ZSwgImZpbGxDb2xvciI6ICIjMDBiNWViIiwgImZpbGxPcGFjaXR5IjogMC43LCAiZmlsbFJ1bGUiOiAiZXZlbm9kZCIsICJsaW5lQ2FwIjogInJvdW5kIiwgImxpbmVKb2luIjogInJvdW5kIiwgIm9wYWNpdHkiOiAxLjAsICJyYWRpdXMiOiA1LCAic3Ryb2tlIjogdHJ1ZSwgIndlaWdodCI6IDN9CiAgICAgICAgICAgICkuYWRkVG8obWFwXzUyM2UwNWEyZjZlMTQ1MTZhNzI0NWM2YzkxM2VmYjA5KTsKICAgICAgICAKICAgIAogICAgICAgIHZhciBwb3B1cF9hOGVjN2UwNjQxOTU0NzJiOTEwNzQxNjkyZGYyNjczNiA9IEwucG9wdXAoeyJtYXhXaWR0aCI6ICIxMDAlIn0pOwoKICAgICAgICAKICAgICAgICAgICAgdmFyIGh0bWxfNThiMzQxYzJlM2YyNDE0ZDhhMTQ4NzUwZTg5MzJiMWEgPSAkKGA8ZGl2IGlkPSJodG1sXzU4YjM0MWMyZTNmMjQxNGQ4YTE0ODc1MGU4OTMyYjFhIiBzdHlsZT0id2lkdGg6IDEwMC4wJTsgaGVpZ2h0OiAxMDAuMCU7Ij5Sb3NlZGFsZSBDbHVzdGVyIDI8L2Rpdj5gKVswXTsKICAgICAgICAgICAgcG9wdXBfYThlYzdlMDY0MTk1NDcyYjkxMDc0MTY5MmRmMjY3MzYuc2V0Q29udGVudChodG1sXzU4YjM0MWMyZTNmMjQxNGQ4YTE0ODc1MGU4OTMyYjFhKTsKICAgICAgICAKCiAgICAgICAgY2lyY2xlX21hcmtlcl9iODU4Yzc2NzVkZjU0MzM3YTJkMGJhNWM4Zjc3MzExYy5iaW5kUG9wdXAocG9wdXBfYThlYzdlMDY0MTk1NDcyYjkxMDc0MTY5MmRmMjY3MzYpCiAgICAgICAgOwoKICAgICAgICAKICAgIAogICAgCiAgICAgICAgICAgIHZhciBjaXJjbGVfbWFya2VyX2RjNjhmODllMTk0NzQ0M2M4Nzk4Y2JmOWZmMjRiYmU3ID0gTC5jaXJjbGVNYXJrZXIoCiAgICAgICAgICAgICAgICBbNDMuNjY3OTY3LCAtNzkuMzY3Njc1M10sCiAgICAgICAgICAgICAgICB7ImJ1YmJsaW5nTW91c2VFdmVudHMiOiB0cnVlLCAiY29sb3IiOiAiI2ZmMDAwMCIsICJkYXNoQXJyYXkiOiBudWxsLCAiZGFzaE9mZnNldCI6IG51bGwsICJmaWxsIjogdHJ1ZSwgImZpbGxDb2xvciI6ICIjZmYwMDAwIiwgImZpbGxPcGFjaXR5IjogMC43LCAiZmlsbFJ1bGUiOiAiZXZlbm9kZCIsICJsaW5lQ2FwIjogInJvdW5kIiwgImxpbmVKb2luIjogInJvdW5kIiwgIm9wYWNpdHkiOiAxLjAsICJyYWRpdXMiOiA1LCAic3Ryb2tlIjogdHJ1ZSwgIndlaWdodCI6IDN9CiAgICAgICAgICAgICkuYWRkVG8obWFwXzUyM2UwNWEyZjZlMTQ1MTZhNzI0NWM2YzkxM2VmYjA5KTsKICAgICAgICAKICAgIAogICAgICAgIHZhciBwb3B1cF80NWMzNDY4ODQzYTM0OWNkOWViYjQ4ZTQ5NTI4Y2Y1MyA9IEwucG9wdXAoeyJtYXhXaWR0aCI6ICIxMDAlIn0pOwoKICAgICAgICAKICAgICAgICAgICAgdmFyIGh0bWxfMmMyMzRlMmJkM2VhNDQ2ZGFhMDNkYTdjNGQyYTEwZDYgPSAkKGA8ZGl2IGlkPSJodG1sXzJjMjM0ZTJiZDNlYTQ0NmRhYTAzZGE3YzRkMmExMGQ2IiBzdHlsZT0id2lkdGg6IDEwMC4wJTsgaGVpZ2h0OiAxMDAuMCU7Ij5TdC4gSmFtZXMgVG93biwgQ2FiYmFnZXRvd24gQ2x1c3RlciAwPC9kaXY+YClbMF07CiAgICAgICAgICAgIHBvcHVwXzQ1YzM0Njg4NDNhMzQ5Y2Q5ZWJiNDhlNDk1MjhjZjUzLnNldENvbnRlbnQoaHRtbF8yYzIzNGUyYmQzZWE0NDZkYWEwM2RhN2M0ZDJhMTBkNik7CiAgICAgICAgCgogICAgICAgIGNpcmNsZV9tYXJrZXJfZGM2OGY4OWUxOTQ3NDQzYzg3OThjYmY5ZmYyNGJiZTcuYmluZFBvcHVwKHBvcHVwXzQ1YzM0Njg4NDNhMzQ5Y2Q5ZWJiNDhlNDk1MjhjZjUzKQogICAgICAgIDsKCiAgICAgICAgCiAgICAKICAgIAogICAgICAgICAgICB2YXIgY2lyY2xlX21hcmtlcl81NzgzMzJjZDgyYmI0MzZjYjUwOTlkOGNiOTgzZjNiNyA9IEwuY2lyY2xlTWFya2VyKAogICAgICAgICAgICAgICAgWzQzLjY2NTg1OTksIC03OS4zODMxNTk5MDAwMDAwMV0sCiAgICAgICAgICAgICAgICB7ImJ1YmJsaW5nTW91c2VFdmVudHMiOiB0cnVlLCAiY29sb3IiOiAiI2ZmMDAwMCIsICJkYXNoQXJyYXkiOiBudWxsLCAiZGFzaE9mZnNldCI6IG51bGwsICJmaWxsIjogdHJ1ZSwgImZpbGxDb2xvciI6ICIjZmYwMDAwIiwgImZpbGxPcGFjaXR5IjogMC43LCAiZmlsbFJ1bGUiOiAiZXZlbm9kZCIsICJsaW5lQ2FwIjogInJvdW5kIiwgImxpbmVKb2luIjogInJvdW5kIiwgIm9wYWNpdHkiOiAxLjAsICJyYWRpdXMiOiA1LCAic3Ryb2tlIjogdHJ1ZSwgIndlaWdodCI6IDN9CiAgICAgICAgICAgICkuYWRkVG8obWFwXzUyM2UwNWEyZjZlMTQ1MTZhNzI0NWM2YzkxM2VmYjA5KTsKICAgICAgICAKICAgIAogICAgICAgIHZhciBwb3B1cF82ZjI3Y2VlM2I0ZGY0ZDYzYTVjZDA3ZDBlYzBjY2M3MyA9IEwucG9wdXAoeyJtYXhXaWR0aCI6ICIxMDAlIn0pOwoKICAgICAgICAKICAgICAgICAgICAgdmFyIGh0bWxfZGZkZDI4ZDkzOTE5NDUxN2FhODM1YWEyZTdkNGNhN2IgPSAkKGA8ZGl2IGlkPSJodG1sX2RmZGQyOGQ5MzkxOTQ1MTdhYTgzNWFhMmU3ZDRjYTdiIiBzdHlsZT0id2lkdGg6IDEwMC4wJTsgaGVpZ2h0OiAxMDAuMCU7Ij5DaHVyY2ggYW5kIFdlbGxlc2xleSBDbHVzdGVyIDA8L2Rpdj5gKVswXTsKICAgICAgICAgICAgcG9wdXBfNmYyN2NlZTNiNGRmNGQ2M2E1Y2QwN2QwZWMwY2NjNzMuc2V0Q29udGVudChodG1sX2RmZGQyOGQ5MzkxOTQ1MTdhYTgzNWFhMmU3ZDRjYTdiKTsKICAgICAgICAKCiAgICAgICAgY2lyY2xlX21hcmtlcl81NzgzMzJjZDgyYmI0MzZjYjUwOTlkOGNiOTgzZjNiNy5iaW5kUG9wdXAocG9wdXBfNmYyN2NlZTNiNGRmNGQ2M2E1Y2QwN2QwZWMwY2NjNzMpCiAgICAgICAgOwoKICAgICAgICAKICAgIAogICAgCiAgICAgICAgICAgIHZhciBjaXJjbGVfbWFya2VyXzhmN2I1ZGE5NTc1ZjQzNzI5ZjM5NmE0ZjY3NTFiOWVhID0gTC5jaXJjbGVNYXJrZXIoCiAgICAgICAgICAgICAgICBbNDMuNjU0MjU5OSwgLTc5LjM2MDYzNTldLAogICAgICAgICAgICAgICAgeyJidWJibGluZ01vdXNlRXZlbnRzIjogdHJ1ZSwgImNvbG9yIjogIiNmZjAwMDAiLCAiZGFzaEFycmF5IjogbnVsbCwgImRhc2hPZmZzZXQiOiBudWxsLCAiZmlsbCI6IHRydWUsICJmaWxsQ29sb3IiOiAiI2ZmMDAwMCIsICJmaWxsT3BhY2l0eSI6IDAuNywgImZpbGxSdWxlIjogImV2ZW5vZGQiLCAibGluZUNhcCI6ICJyb3VuZCIsICJsaW5lSm9pbiI6ICJyb3VuZCIsICJvcGFjaXR5IjogMS4wLCAicmFkaXVzIjogNSwgInN0cm9rZSI6IHRydWUsICJ3ZWlnaHQiOiAzfQogICAgICAgICAgICApLmFkZFRvKG1hcF81MjNlMDVhMmY2ZTE0NTE2YTcyNDVjNmM5MTNlZmIwOSk7CiAgICAgICAgCiAgICAKICAgICAgICB2YXIgcG9wdXBfMDhjMjdkZTBkZTRkNGRhZGIwYTZiMjhjMmRiNmZkZTggPSBMLnBvcHVwKHsibWF4V2lkdGgiOiAiMTAwJSJ9KTsKCiAgICAgICAgCiAgICAgICAgICAgIHZhciBodG1sXzU4M2MwOGEwYmY3NDQ2ODRiYzU0ZTkzNjc3ZmM3MDhhID0gJChgPGRpdiBpZD0iaHRtbF81ODNjMDhhMGJmNzQ0Njg0YmM1NGU5MzY3N2ZjNzA4YSIgc3R5bGU9IndpZHRoOiAxMDAuMCU7IGhlaWdodDogMTAwLjAlOyI+UmVnZW50IFBhcmssIEhhcmJvdXJmcm9udCBDbHVzdGVyIDA8L2Rpdj5gKVswXTsKICAgICAgICAgICAgcG9wdXBfMDhjMjdkZTBkZTRkNGRhZGIwYTZiMjhjMmRiNmZkZTguc2V0Q29udGVudChodG1sXzU4M2MwOGEwYmY3NDQ2ODRiYzU0ZTkzNjc3ZmM3MDhhKTsKICAgICAgICAKCiAgICAgICAgY2lyY2xlX21hcmtlcl84ZjdiNWRhOTU3NWY0MzcyOWYzOTZhNGY2NzUxYjllYS5iaW5kUG9wdXAocG9wdXBfMDhjMjdkZTBkZTRkNGRhZGIwYTZiMjhjMmRiNmZkZTgpCiAgICAgICAgOwoKICAgICAgICAKICAgIAogICAgCiAgICAgICAgICAgIHZhciBjaXJjbGVfbWFya2VyXzlhNDFjMjljZjI1YTRiMzk5N2VlMTc2MmJlZDAyNzNiID0gTC5jaXJjbGVNYXJrZXIoCiAgICAgICAgICAgICAgICBbNDMuNjU3MTYxOCwgLTc5LjM3ODkzNzA5OTk5OTk5XSwKICAgICAgICAgICAgICAgIHsiYnViYmxpbmdNb3VzZUV2ZW50cyI6IHRydWUsICJjb2xvciI6ICIjZmYwMDAwIiwgImRhc2hBcnJheSI6IG51bGwsICJkYXNoT2Zmc2V0IjogbnVsbCwgImZpbGwiOiB0cnVlLCAiZmlsbENvbG9yIjogIiNmZjAwMDAiLCAiZmlsbE9wYWNpdHkiOiAwLjcsICJmaWxsUnVsZSI6ICJldmVub2RkIiwgImxpbmVDYXAiOiAicm91bmQiLCAibGluZUpvaW4iOiAicm91bmQiLCAib3BhY2l0eSI6IDEuMCwgInJhZGl1cyI6IDUsICJzdHJva2UiOiB0cnVlLCAid2VpZ2h0IjogM30KICAgICAgICAgICAgKS5hZGRUbyhtYXBfNTIzZTA1YTJmNmUxNDUxNmE3MjQ1YzZjOTEzZWZiMDkpOwogICAgICAgIAogICAgCiAgICAgICAgdmFyIHBvcHVwXzAxNDhlNjc0OGI1ZTRhOTJhYmU4MDU2MDg4OTkxODJkID0gTC5wb3B1cCh7Im1heFdpZHRoIjogIjEwMCUifSk7CgogICAgICAgIAogICAgICAgICAgICB2YXIgaHRtbF82N2ZiOTFiYTk2MDA0YTJkYTYwMmZhODFhMjAyNjA2YSA9ICQoYDxkaXYgaWQ9Imh0bWxfNjdmYjkxYmE5NjAwNGEyZGE2MDJmYTgxYTIwMjYwNmEiIHN0eWxlPSJ3aWR0aDogMTAwLjAlOyBoZWlnaHQ6IDEwMC4wJTsiPkdhcmRlbiBEaXN0cmljdCwgUnllcnNvbiBDbHVzdGVyIDA8L2Rpdj5gKVswXTsKICAgICAgICAgICAgcG9wdXBfMDE0OGU2NzQ4YjVlNGE5MmFiZTgwNTYwODg5OTE4MmQuc2V0Q29udGVudChodG1sXzY3ZmI5MWJhOTYwMDRhMmRhNjAyZmE4MWEyMDI2MDZhKTsKICAgICAgICAKCiAgICAgICAgY2lyY2xlX21hcmtlcl85YTQxYzI5Y2YyNWE0YjM5OTdlZTE3NjJiZWQwMjczYi5iaW5kUG9wdXAocG9wdXBfMDE0OGU2NzQ4YjVlNGE5MmFiZTgwNTYwODg5OTE4MmQpCiAgICAgICAgOwoKICAgICAgICAKICAgIAogICAgCiAgICAgICAgICAgIHZhciBjaXJjbGVfbWFya2VyXzk3OWNkNjU5NTM5YTRjMjA4OTRiYTU2NzIyNGI0ZDQyID0gTC5jaXJjbGVNYXJrZXIoCiAgICAgICAgICAgICAgICBbNDMuNjUxNDkzOSwgLTc5LjM3NTQxNzldLAogICAgICAgICAgICAgICAgeyJidWJibGluZ01vdXNlRXZlbnRzIjogdHJ1ZSwgImNvbG9yIjogIiNmZjAwMDAiLCAiZGFzaEFycmF5IjogbnVsbCwgImRhc2hPZmZzZXQiOiBudWxsLCAiZmlsbCI6IHRydWUsICJmaWxsQ29sb3IiOiAiI2ZmMDAwMCIsICJmaWxsT3BhY2l0eSI6IDAuNywgImZpbGxSdWxlIjogImV2ZW5vZGQiLCAibGluZUNhcCI6ICJyb3VuZCIsICJsaW5lSm9pbiI6ICJyb3VuZCIsICJvcGFjaXR5IjogMS4wLCAicmFkaXVzIjogNSwgInN0cm9rZSI6IHRydWUsICJ3ZWlnaHQiOiAzfQogICAgICAgICAgICApLmFkZFRvKG1hcF81MjNlMDVhMmY2ZTE0NTE2YTcyNDVjNmM5MTNlZmIwOSk7CiAgICAgICAgCiAgICAKICAgICAgICB2YXIgcG9wdXBfMzAyNmIwMDVjNzQwNDNjNzg2ZGNlYzhmYTQ2NGRjZjQgPSBMLnBvcHVwKHsibWF4V2lkdGgiOiAiMTAwJSJ9KTsKCiAgICAgICAgCiAgICAgICAgICAgIHZhciBodG1sX2NjNzI0NjFhMjlhNzQzNTM5OTg5OTE5ZGJkZDdkMjFiID0gJChgPGRpdiBpZD0iaHRtbF9jYzcyNDYxYTI5YTc0MzUzOTk4OTkxOWRiZGQ3ZDIxYiIgc3R5bGU9IndpZHRoOiAxMDAuMCU7IGhlaWdodDogMTAwLjAlOyI+U3QuIEphbWVzIFRvd24gQ2x1c3RlciAwPC9kaXY+YClbMF07CiAgICAgICAgICAgIHBvcHVwXzMwMjZiMDA1Yzc0MDQzYzc4NmRjZWM4ZmE0NjRkY2Y0LnNldENvbnRlbnQoaHRtbF9jYzcyNDYxYTI5YTc0MzUzOTk4OTkxOWRiZGQ3ZDIxYik7CiAgICAgICAgCgogICAgICAgIGNpcmNsZV9tYXJrZXJfOTc5Y2Q2NTk1MzlhNGMyMDg5NGJhNTY3MjI0YjRkNDIuYmluZFBvcHVwKHBvcHVwXzMwMjZiMDA1Yzc0MDQzYzc4NmRjZWM4ZmE0NjRkY2Y0KQogICAgICAgIDsKCiAgICAgICAgCiAgICAKICAgIAogICAgICAgICAgICB2YXIgY2lyY2xlX21hcmtlcl8xNGIxOTY3NDY4MzM0ZjVmYWViNGI2YWQzMGE5ZDYyMyA9IEwuY2lyY2xlTWFya2VyKAogICAgICAgICAgICAgICAgWzQzLjY0NDc3MDc5OTk5OTk5NiwgLTc5LjM3MzMwNjRdLAogICAgICAgICAgICAgICAgeyJidWJibGluZ01vdXNlRXZlbnRzIjogdHJ1ZSwgImNvbG9yIjogIiNmZjAwMDAiLCAiZGFzaEFycmF5IjogbnVsbCwgImRhc2hPZmZzZXQiOiBudWxsLCAiZmlsbCI6IHRydWUsICJmaWxsQ29sb3IiOiAiI2ZmMDAwMCIsICJmaWxsT3BhY2l0eSI6IDAuNywgImZpbGxSdWxlIjogImV2ZW5vZGQiLCAibGluZUNhcCI6ICJyb3VuZCIsICJsaW5lSm9pbiI6ICJyb3VuZCIsICJvcGFjaXR5IjogMS4wLCAicmFkaXVzIjogNSwgInN0cm9rZSI6IHRydWUsICJ3ZWlnaHQiOiAzfQogICAgICAgICAgICApLmFkZFRvKG1hcF81MjNlMDVhMmY2ZTE0NTE2YTcyNDVjNmM5MTNlZmIwOSk7CiAgICAgICAgCiAgICAKICAgICAgICB2YXIgcG9wdXBfODQ3OTBhMTQ3ZDEwNGMwY2FlNmFkNjU1ZmVmNGM2YzYgPSBMLnBvcHVwKHsibWF4V2lkdGgiOiAiMTAwJSJ9KTsKCiAgICAgICAgCiAgICAgICAgICAgIHZhciBodG1sX2FhYmNiZTY5Mzc1NzQ4ZTZiMzFhYTM2MDRkZmU3MmVmID0gJChgPGRpdiBpZD0iaHRtbF9hYWJjYmU2OTM3NTc0OGU2YjMxYWEzNjA0ZGZlNzJlZiIgc3R5bGU9IndpZHRoOiAxMDAuMCU7IGhlaWdodDogMTAwLjAlOyI+QmVyY3p5IFBhcmsgQ2x1c3RlciAwPC9kaXY+YClbMF07CiAgICAgICAgICAgIHBvcHVwXzg0NzkwYTE0N2QxMDRjMGNhZTZhZDY1NWZlZjRjNmM2LnNldENvbnRlbnQoaHRtbF9hYWJjYmU2OTM3NTc0OGU2YjMxYWEzNjA0ZGZlNzJlZik7CiAgICAgICAgCgogICAgICAgIGNpcmNsZV9tYXJrZXJfMTRiMTk2NzQ2ODMzNGY1ZmFlYjRiNmFkMzBhOWQ2MjMuYmluZFBvcHVwKHBvcHVwXzg0NzkwYTE0N2QxMDRjMGNhZTZhZDY1NWZlZjRjNmM2KQogICAgICAgIDsKCiAgICAgICAgCiAgICAKICAgIAogICAgICAgICAgICB2YXIgY2lyY2xlX21hcmtlcl9hYTJiZGVkNDMyZmQ0YmE0YjZmZDY2NWUwZTk0YWMyMSA9IEwuY2lyY2xlTWFya2VyKAogICAgICAgICAgICAgICAgWzQzLjY1Nzk1MjQsIC03OS4zODczODI2XSwKICAgICAgICAgICAgICAgIHsiYnViYmxpbmdNb3VzZUV2ZW50cyI6IHRydWUsICJjb2xvciI6ICIjZmYwMDAwIiwgImRhc2hBcnJheSI6IG51bGwsICJkYXNoT2Zmc2V0IjogbnVsbCwgImZpbGwiOiB0cnVlLCAiZmlsbENvbG9yIjogIiNmZjAwMDAiLCAiZmlsbE9wYWNpdHkiOiAwLjcsICJmaWxsUnVsZSI6ICJldmVub2RkIiwgImxpbmVDYXAiOiAicm91bmQiLCAibGluZUpvaW4iOiAicm91bmQiLCAib3BhY2l0eSI6IDEuMCwgInJhZGl1cyI6IDUsICJzdHJva2UiOiB0cnVlLCAid2VpZ2h0IjogM30KICAgICAgICAgICAgKS5hZGRUbyhtYXBfNTIzZTA1YTJmNmUxNDUxNmE3MjQ1YzZjOTEzZWZiMDkpOwogICAgICAgIAogICAgCiAgICAgICAgdmFyIHBvcHVwXzVmMzA5MWE0N2FlMTQxZGM4MTYxNjZjMTJmNGIxZmM3ID0gTC5wb3B1cCh7Im1heFdpZHRoIjogIjEwMCUifSk7CgogICAgICAgIAogICAgICAgICAgICB2YXIgaHRtbF83MzgxYWVkODg1ZTY0NTM1OTJhNzA1MzNkNGUyZWY4YiA9ICQoYDxkaXYgaWQ9Imh0bWxfNzM4MWFlZDg4NWU2NDUzNTkyYTcwNTMzZDRlMmVmOGIiIHN0eWxlPSJ3aWR0aDogMTAwLjAlOyBoZWlnaHQ6IDEwMC4wJTsiPkNlbnRyYWwgQmF5IFN0cmVldCBDbHVzdGVyIDA8L2Rpdj5gKVswXTsKICAgICAgICAgICAgcG9wdXBfNWYzMDkxYTQ3YWUxNDFkYzgxNjE2NmMxMmY0YjFmYzcuc2V0Q29udGVudChodG1sXzczODFhZWQ4ODVlNjQ1MzU5MmE3MDUzM2Q0ZTJlZjhiKTsKICAgICAgICAKCiAgICAgICAgY2lyY2xlX21hcmtlcl9hYTJiZGVkNDMyZmQ0YmE0YjZmZDY2NWUwZTk0YWMyMS5iaW5kUG9wdXAocG9wdXBfNWYzMDkxYTQ3YWUxNDFkYzgxNjE2NmMxMmY0YjFmYzcpCiAgICAgICAgOwoKICAgICAgICAKICAgIAogICAgCiAgICAgICAgICAgIHZhciBjaXJjbGVfbWFya2VyXzZmOGZlYWU4NDYzZDQ5MjY4ZjgwNjMxMGIxOTY1ZDcwID0gTC5jaXJjbGVNYXJrZXIoCiAgICAgICAgICAgICAgICBbNDMuNjUwNTcxMjAwMDAwMDEsIC03OS4zODQ1Njc1XSwKICAgICAgICAgICAgICAgIHsiYnViYmxpbmdNb3VzZUV2ZW50cyI6IHRydWUsICJjb2xvciI6ICIjZmYwMDAwIiwgImRhc2hBcnJheSI6IG51bGwsICJkYXNoT2Zmc2V0IjogbnVsbCwgImZpbGwiOiB0cnVlLCAiZmlsbENvbG9yIjogIiNmZjAwMDAiLCAiZmlsbE9wYWNpdHkiOiAwLjcsICJmaWxsUnVsZSI6ICJldmVub2RkIiwgImxpbmVDYXAiOiAicm91bmQiLCAibGluZUpvaW4iOiAicm91bmQiLCAib3BhY2l0eSI6IDEuMCwgInJhZGl1cyI6IDUsICJzdHJva2UiOiB0cnVlLCAid2VpZ2h0IjogM30KICAgICAgICAgICAgKS5hZGRUbyhtYXBfNTIzZTA1YTJmNmUxNDUxNmE3MjQ1YzZjOTEzZWZiMDkpOwogICAgICAgIAogICAgCiAgICAgICAgdmFyIHBvcHVwXzFmYTBjZjQyNTZiMDRjMzI5OWZlYzQyNzAyNWRjOGM3ID0gTC5wb3B1cCh7Im1heFdpZHRoIjogIjEwMCUifSk7CgogICAgICAgIAogICAgICAgICAgICB2YXIgaHRtbF8zYjY5MDIzNGYxNzM0YTQwOGZlZmRiZGMwNjA3Y2IxMiA9ICQoYDxkaXYgaWQ9Imh0bWxfM2I2OTAyMzRmMTczNGE0MDhmZWZkYmRjMDYwN2NiMTIiIHN0eWxlPSJ3aWR0aDogMTAwLjAlOyBoZWlnaHQ6IDEwMC4wJTsiPlJpY2htb25kLCBBZGVsYWlkZSwgS2luZyBDbHVzdGVyIDA8L2Rpdj5gKVswXTsKICAgICAgICAgICAgcG9wdXBfMWZhMGNmNDI1NmIwNGMzMjk5ZmVjNDI3MDI1ZGM4Yzcuc2V0Q29udGVudChodG1sXzNiNjkwMjM0ZjE3MzRhNDA4ZmVmZGJkYzA2MDdjYjEyKTsKICAgICAgICAKCiAgICAgICAgY2lyY2xlX21hcmtlcl82ZjhmZWFlODQ2M2Q0OTI2OGY4MDYzMTBiMTk2NWQ3MC5iaW5kUG9wdXAocG9wdXBfMWZhMGNmNDI1NmIwNGMzMjk5ZmVjNDI3MDI1ZGM4YzcpCiAgICAgICAgOwoKICAgICAgICAKICAgIAogICAgCiAgICAgICAgICAgIHZhciBjaXJjbGVfbWFya2VyXzMzZGJiNjUzM2ZlODQ2ZmY5MTU3YTQ0NDMyZjU0YzhhID0gTC5jaXJjbGVNYXJrZXIoCiAgICAgICAgICAgICAgICBbNDMuNjQwODE1NywgLTc5LjM4MTc1MjI5OTk5OTk5XSwKICAgICAgICAgICAgICAgIHsiYnViYmxpbmdNb3VzZUV2ZW50cyI6IHRydWUsICJjb2xvciI6ICIjZmYwMDAwIiwgImRhc2hBcnJheSI6IG51bGwsICJkYXNoT2Zmc2V0IjogbnVsbCwgImZpbGwiOiB0cnVlLCAiZmlsbENvbG9yIjogIiNmZjAwMDAiLCAiZmlsbE9wYWNpdHkiOiAwLjcsICJmaWxsUnVsZSI6ICJldmVub2RkIiwgImxpbmVDYXAiOiAicm91bmQiLCAibGluZUpvaW4iOiAicm91bmQiLCAib3BhY2l0eSI6IDEuMCwgInJhZGl1cyI6IDUsICJzdHJva2UiOiB0cnVlLCAid2VpZ2h0IjogM30KICAgICAgICAgICAgKS5hZGRUbyhtYXBfNTIzZTA1YTJmNmUxNDUxNmE3MjQ1YzZjOTEzZWZiMDkpOwogICAgICAgIAogICAgCiAgICAgICAgdmFyIHBvcHVwXzU5NGIwYzUzMzIwMzRjNmI5ZjY3MjU4YjE4YjFlY2U1ID0gTC5wb3B1cCh7Im1heFdpZHRoIjogIjEwMCUifSk7CgogICAgICAgIAogICAgICAgICAgICB2YXIgaHRtbF9mMDg1YjYwZTY0N2Q0YzM1OTNmZWFkY2ZkZTQyNWI0NiA9ICQoYDxkaXYgaWQ9Imh0bWxfZjA4NWI2MGU2NDdkNGMzNTkzZmVhZGNmZGU0MjViNDYiIHN0eWxlPSJ3aWR0aDogMTAwLjAlOyBoZWlnaHQ6IDEwMC4wJTsiPkhhcmJvdXJmcm9udCBFYXN0LCBVbmlvbiBTdGF0aW9uLCBUb3JvbnRvIElzbGFuZHMgQ2x1c3RlciAwPC9kaXY+YClbMF07CiAgICAgICAgICAgIHBvcHVwXzU5NGIwYzUzMzIwMzRjNmI5ZjY3MjU4YjE4YjFlY2U1LnNldENvbnRlbnQoaHRtbF9mMDg1YjYwZTY0N2Q0YzM1OTNmZWFkY2ZkZTQyNWI0Nik7CiAgICAgICAgCgogICAgICAgIGNpcmNsZV9tYXJrZXJfMzNkYmI2NTMzZmU4NDZmZjkxNTdhNDQ0MzJmNTRjOGEuYmluZFBvcHVwKHBvcHVwXzU5NGIwYzUzMzIwMzRjNmI5ZjY3MjU4YjE4YjFlY2U1KQogICAgICAgIDsKCiAgICAgICAgCiAgICAKICAgIAogICAgICAgICAgICB2YXIgY2lyY2xlX21hcmtlcl83Mjg5MDkzY2M3MTI0YjdhOTBlNDIwMzliMDY2NWY4MiA9IEwuY2lyY2xlTWFya2VyKAogICAgICAgICAgICAgICAgWzQzLjY0NzE3NjgsIC03OS4zODE1NzY0MDAwMDAwMV0sCiAgICAgICAgICAgICAgICB7ImJ1YmJsaW5nTW91c2VFdmVudHMiOiB0cnVlLCAiY29sb3IiOiAiI2ZmMDAwMCIsICJkYXNoQXJyYXkiOiBudWxsLCAiZGFzaE9mZnNldCI6IG51bGwsICJmaWxsIjogdHJ1ZSwgImZpbGxDb2xvciI6ICIjZmYwMDAwIiwgImZpbGxPcGFjaXR5IjogMC43LCAiZmlsbFJ1bGUiOiAiZXZlbm9kZCIsICJsaW5lQ2FwIjogInJvdW5kIiwgImxpbmVKb2luIjogInJvdW5kIiwgIm9wYWNpdHkiOiAxLjAsICJyYWRpdXMiOiA1LCAic3Ryb2tlIjogdHJ1ZSwgIndlaWdodCI6IDN9CiAgICAgICAgICAgICkuYWRkVG8obWFwXzUyM2UwNWEyZjZlMTQ1MTZhNzI0NWM2YzkxM2VmYjA5KTsKICAgICAgICAKICAgIAogICAgICAgIHZhciBwb3B1cF8xNmExNjNmNWFlNzg0ODkzYmIwMTA0MWNhN2Q2ZjRlYyA9IEwucG9wdXAoeyJtYXhXaWR0aCI6ICIxMDAlIn0pOwoKICAgICAgICAKICAgICAgICAgICAgdmFyIGh0bWxfMTkwZjZkMmIyOTYyNDQ2Y2E0ZTNhZjNkMGNhMmE2N2IgPSAkKGA8ZGl2IGlkPSJodG1sXzE5MGY2ZDJiMjk2MjQ0NmNhNGUzYWYzZDBjYTJhNjdiIiBzdHlsZT0id2lkdGg6IDEwMC4wJTsgaGVpZ2h0OiAxMDAuMCU7Ij5Ub3JvbnRvIERvbWluaW9uIENlbnRyZSwgRGVzaWduIEV4Y2hhbmdlIENsdXN0ZXIgMDwvZGl2PmApWzBdOwogICAgICAgICAgICBwb3B1cF8xNmExNjNmNWFlNzg0ODkzYmIwMTA0MWNhN2Q2ZjRlYy5zZXRDb250ZW50KGh0bWxfMTkwZjZkMmIyOTYyNDQ2Y2E0ZTNhZjNkMGNhMmE2N2IpOwogICAgICAgIAoKICAgICAgICBjaXJjbGVfbWFya2VyXzcyODkwOTNjYzcxMjRiN2E5MGU0MjAzOWIwNjY1ZjgyLmJpbmRQb3B1cChwb3B1cF8xNmExNjNmNWFlNzg0ODkzYmIwMTA0MWNhN2Q2ZjRlYykKICAgICAgICA7CgogICAgICAgIAogICAgCiAgICAKICAgICAgICAgICAgdmFyIGNpcmNsZV9tYXJrZXJfODAxNGE3MzdmMjRkNDgzMDllYjlkYTBiYjUwZWI4NDQgPSBMLmNpcmNsZU1hcmtlcigKICAgICAgICAgICAgICAgIFs0My42NDgxOTg1LCAtNzkuMzc5ODE2OTAwMDAwMDFdLAogICAgICAgICAgICAgICAgeyJidWJibGluZ01vdXNlRXZlbnRzIjogdHJ1ZSwgImNvbG9yIjogIiNmZjAwMDAiLCAiZGFzaEFycmF5IjogbnVsbCwgImRhc2hPZmZzZXQiOiBudWxsLCAiZmlsbCI6IHRydWUsICJmaWxsQ29sb3IiOiAiI2ZmMDAwMCIsICJmaWxsT3BhY2l0eSI6IDAuNywgImZpbGxSdWxlIjogImV2ZW5vZGQiLCAibGluZUNhcCI6ICJyb3VuZCIsICJsaW5lSm9pbiI6ICJyb3VuZCIsICJvcGFjaXR5IjogMS4wLCAicmFkaXVzIjogNSwgInN0cm9rZSI6IHRydWUsICJ3ZWlnaHQiOiAzfQogICAgICAgICAgICApLmFkZFRvKG1hcF81MjNlMDVhMmY2ZTE0NTE2YTcyNDVjNmM5MTNlZmIwOSk7CiAgICAgICAgCiAgICAKICAgICAgICB2YXIgcG9wdXBfOTA4Yjc3YmEyNzg4NGY4YThkZWFjZjJkODRlZTcyMDAgPSBMLnBvcHVwKHsibWF4V2lkdGgiOiAiMTAwJSJ9KTsKCiAgICAgICAgCiAgICAgICAgICAgIHZhciBodG1sX2JhZDE4NTJjZmQ5YTQ2OWVhMzExYmQ5YTNjZDY0ZWE0ID0gJChgPGRpdiBpZD0iaHRtbF9iYWQxODUyY2ZkOWE0NjllYTMxMWJkOWEzY2Q2NGVhNCIgc3R5bGU9IndpZHRoOiAxMDAuMCU7IGhlaWdodDogMTAwLjAlOyI+Q29tbWVyY2UgQ291cnQsIFZpY3RvcmlhIEhvdGVsIENsdXN0ZXIgMDwvZGl2PmApWzBdOwogICAgICAgICAgICBwb3B1cF85MDhiNzdiYTI3ODg0ZjhhOGRlYWNmMmQ4NGVlNzIwMC5zZXRDb250ZW50KGh0bWxfYmFkMTg1MmNmZDlhNDY5ZWEzMTFiZDlhM2NkNjRlYTQpOwogICAgICAgIAoKICAgICAgICBjaXJjbGVfbWFya2VyXzgwMTRhNzM3ZjI0ZDQ4MzA5ZWI5ZGEwYmI1MGViODQ0LmJpbmRQb3B1cChwb3B1cF85MDhiNzdiYTI3ODg0ZjhhOGRlYWNmMmQ4NGVlNzIwMCkKICAgICAgICA7CgogICAgICAgIAogICAgCiAgICAKICAgICAgICAgICAgdmFyIGNpcmNsZV9tYXJrZXJfNGYyYTRjMTc3ZmEzNDU1OWJiZDQ0NmUyYWE4Y2QwNTkgPSBMLmNpcmNsZU1hcmtlcigKICAgICAgICAgICAgICAgIFs0My43MTE2OTQ4LCAtNzkuNDE2OTM1NTk5OTk5OTldLAogICAgICAgICAgICAgICAgeyJidWJibGluZ01vdXNlRXZlbnRzIjogdHJ1ZSwgImNvbG9yIjogIiM4MGZmYjQiLCAiZGFzaEFycmF5IjogbnVsbCwgImRhc2hPZmZzZXQiOiBudWxsLCAiZmlsbCI6IHRydWUsICJmaWxsQ29sb3IiOiAiIzgwZmZiNCIsICJmaWxsT3BhY2l0eSI6IDAuNywgImZpbGxSdWxlIjogImV2ZW5vZGQiLCAibGluZUNhcCI6ICJyb3VuZCIsICJsaW5lSm9pbiI6ICJyb3VuZCIsICJvcGFjaXR5IjogMS4wLCAicmFkaXVzIjogNSwgInN0cm9rZSI6IHRydWUsICJ3ZWlnaHQiOiAzfQogICAgICAgICAgICApLmFkZFRvKG1hcF81MjNlMDVhMmY2ZTE0NTE2YTcyNDVjNmM5MTNlZmIwOSk7CiAgICAgICAgCiAgICAKICAgICAgICB2YXIgcG9wdXBfZGFjMzk3YzNlOWY1NGVhYzgwNWZkZmJjNTYwZjVmN2YgPSBMLnBvcHVwKHsibWF4V2lkdGgiOiAiMTAwJSJ9KTsKCiAgICAgICAgCiAgICAgICAgICAgIHZhciBodG1sXzhlYjRjNGQxZDRmNDQwNDg4YjJiMzY1OGQyNDE3ZjRhID0gJChgPGRpdiBpZD0iaHRtbF84ZWI0YzRkMWQ0ZjQ0MDQ4OGIyYjM2NThkMjQxN2Y0YSIgc3R5bGU9IndpZHRoOiAxMDAuMCU7IGhlaWdodDogMTAwLjAlOyI+Um9zZWxhd24gQ2x1c3RlciAzPC9kaXY+YClbMF07CiAgICAgICAgICAgIHBvcHVwX2RhYzM5N2MzZTlmNTRlYWM4MDVmZGZiYzU2MGY1ZjdmLnNldENvbnRlbnQoaHRtbF84ZWI0YzRkMWQ0ZjQ0MDQ4OGIyYjM2NThkMjQxN2Y0YSk7CiAgICAgICAgCgogICAgICAgIGNpcmNsZV9tYXJrZXJfNGYyYTRjMTc3ZmEzNDU1OWJiZDQ0NmUyYWE4Y2QwNTkuYmluZFBvcHVwKHBvcHVwX2RhYzM5N2MzZTlmNTRlYWM4MDVmZGZiYzU2MGY1ZjdmKQogICAgICAgIDsKCiAgICAgICAgCiAgICAKICAgIAogICAgICAgICAgICB2YXIgY2lyY2xlX21hcmtlcl9mMmM5NzVlZDI2NGE0NDVjODljYmFlMzJlMGVmNzYyYyA9IEwuY2lyY2xlTWFya2VyKAogICAgICAgICAgICAgICAgWzQzLjY5Njk0NzYsIC03OS40MTEzMDcyMDAwMDAwMV0sCiAgICAgICAgICAgICAgICB7ImJ1YmJsaW5nTW91c2VFdmVudHMiOiB0cnVlLCAiY29sb3IiOiAiIzAwYjVlYiIsICJkYXNoQXJyYXkiOiBudWxsLCAiZGFzaE9mZnNldCI6IG51bGwsICJmaWxsIjogdHJ1ZSwgImZpbGxDb2xvciI6ICIjMDBiNWViIiwgImZpbGxPcGFjaXR5IjogMC43LCAiZmlsbFJ1bGUiOiAiZXZlbm9kZCIsICJsaW5lQ2FwIjogInJvdW5kIiwgImxpbmVKb2luIjogInJvdW5kIiwgIm9wYWNpdHkiOiAxLjAsICJyYWRpdXMiOiA1LCAic3Ryb2tlIjogdHJ1ZSwgIndlaWdodCI6IDN9CiAgICAgICAgICAgICkuYWRkVG8obWFwXzUyM2UwNWEyZjZlMTQ1MTZhNzI0NWM2YzkxM2VmYjA5KTsKICAgICAgICAKICAgIAogICAgICAgIHZhciBwb3B1cF8yOTY1N2U3ZDM1Yzk0NzBkOWY0ZjViMjE2M2ZlOTQ0ZSA9IEwucG9wdXAoeyJtYXhXaWR0aCI6ICIxMDAlIn0pOwoKICAgICAgICAKICAgICAgICAgICAgdmFyIGh0bWxfNTc2NDY5MDJlOTBjNDA3OGJlY2UwYzBiYmM2Y2RmZWYgPSAkKGA8ZGl2IGlkPSJodG1sXzU3NjQ2OTAyZTkwYzQwNzhiZWNlMGMwYmJjNmNkZmVmIiBzdHlsZT0id2lkdGg6IDEwMC4wJTsgaGVpZ2h0OiAxMDAuMCU7Ij5Gb3Jlc3QgSGlsbCBOb3J0aCAmYW1wOyBXZXN0LCBGb3Jlc3QgSGlsbCBSb2FkIFBhcmsgQ2x1c3RlciAyPC9kaXY+YClbMF07CiAgICAgICAgICAgIHBvcHVwXzI5NjU3ZTdkMzVjOTQ3MGQ5ZjRmNWIyMTYzZmU5NDRlLnNldENvbnRlbnQoaHRtbF81NzY0NjkwMmU5MGM0MDc4YmVjZTBjMGJiYzZjZGZlZik7CiAgICAgICAgCgogICAgICAgIGNpcmNsZV9tYXJrZXJfZjJjOTc1ZWQyNjRhNDQ1Yzg5Y2JhZTMyZTBlZjc2MmMuYmluZFBvcHVwKHBvcHVwXzI5NjU3ZTdkMzVjOTQ3MGQ5ZjRmNWIyMTYzZmU5NDRlKQogICAgICAgIDsKCiAgICAgICAgCiAgICAKICAgIAogICAgICAgICAgICB2YXIgY2lyY2xlX21hcmtlcl83MWJlMzA3YTY2M2Q0NTYwODNmZDJkYmM1NTI4ZTIyOCA9IEwuY2lyY2xlTWFya2VyKAogICAgICAgICAgICAgICAgWzQzLjY3MjcwOTcsIC03OS40MDU2Nzg0MDAwMDAwMV0sCiAgICAgICAgICAgICAgICB7ImJ1YmJsaW5nTW91c2VFdmVudHMiOiB0cnVlLCAiY29sb3IiOiAiI2ZmMDAwMCIsICJkYXNoQXJyYXkiOiBudWxsLCAiZGFzaE9mZnNldCI6IG51bGwsICJmaWxsIjogdHJ1ZSwgImZpbGxDb2xvciI6ICIjZmYwMDAwIiwgImZpbGxPcGFjaXR5IjogMC43LCAiZmlsbFJ1bGUiOiAiZXZlbm9kZCIsICJsaW5lQ2FwIjogInJvdW5kIiwgImxpbmVKb2luIjogInJvdW5kIiwgIm9wYWNpdHkiOiAxLjAsICJyYWRpdXMiOiA1LCAic3Ryb2tlIjogdHJ1ZSwgIndlaWdodCI6IDN9CiAgICAgICAgICAgICkuYWRkVG8obWFwXzUyM2UwNWEyZjZlMTQ1MTZhNzI0NWM2YzkxM2VmYjA5KTsKICAgICAgICAKICAgIAogICAgICAgIHZhciBwb3B1cF8xMzY0MTM4OTFmNmQ0MmVjOTBlMjIyZTIxMDgwYjdkMyA9IEwucG9wdXAoeyJtYXhXaWR0aCI6ICIxMDAlIn0pOwoKICAgICAgICAKICAgICAgICAgICAgdmFyIGh0bWxfZDc3MDIxYmRiNDk3NDUzNGIxOWJmZjc0NGNmYTUyNjQgPSAkKGA8ZGl2IGlkPSJodG1sX2Q3NzAyMWJkYjQ5NzQ1MzRiMTliZmY3NDRjZmE1MjY0IiBzdHlsZT0id2lkdGg6IDEwMC4wJTsgaGVpZ2h0OiAxMDAuMCU7Ij5UaGUgQW5uZXgsIE5vcnRoIE1pZHRvd24sIFlvcmt2aWxsZSBDbHVzdGVyIDA8L2Rpdj5gKVswXTsKICAgICAgICAgICAgcG9wdXBfMTM2NDEzODkxZjZkNDJlYzkwZTIyMmUyMTA4MGI3ZDMuc2V0Q29udGVudChodG1sX2Q3NzAyMWJkYjQ5NzQ1MzRiMTliZmY3NDRjZmE1MjY0KTsKICAgICAgICAKCiAgICAgICAgY2lyY2xlX21hcmtlcl83MWJlMzA3YTY2M2Q0NTYwODNmZDJkYmM1NTI4ZTIyOC5iaW5kUG9wdXAocG9wdXBfMTM2NDEzODkxZjZkNDJlYzkwZTIyMmUyMTA4MGI3ZDMpCiAgICAgICAgOwoKICAgICAgICAKICAgIAogICAgCiAgICAgICAgICAgIHZhciBjaXJjbGVfbWFya2VyXzU2MDdlMzBmN2Q1NjQ4MzhiYTNkMmRhYzNiMWJjOWQyID0gTC5jaXJjbGVNYXJrZXIoCiAgICAgICAgICAgICAgICBbNDMuNjYyNjk1NiwgLTc5LjQwMDA0OTNdLAogICAgICAgICAgICAgICAgeyJidWJibGluZ01vdXNlRXZlbnRzIjogdHJ1ZSwgImNvbG9yIjogIiNmZjAwMDAiLCAiZGFzaEFycmF5IjogbnVsbCwgImRhc2hPZmZzZXQiOiBudWxsLCAiZmlsbCI6IHRydWUsICJmaWxsQ29sb3IiOiAiI2ZmMDAwMCIsICJmaWxsT3BhY2l0eSI6IDAuNywgImZpbGxSdWxlIjogImV2ZW5vZGQiLCAibGluZUNhcCI6ICJyb3VuZCIsICJsaW5lSm9pbiI6ICJyb3VuZCIsICJvcGFjaXR5IjogMS4wLCAicmFkaXVzIjogNSwgInN0cm9rZSI6IHRydWUsICJ3ZWlnaHQiOiAzfQogICAgICAgICAgICApLmFkZFRvKG1hcF81MjNlMDVhMmY2ZTE0NTE2YTcyNDVjNmM5MTNlZmIwOSk7CiAgICAgICAgCiAgICAKICAgICAgICB2YXIgcG9wdXBfNGUzOTNiZDk4ZjFkNDgxZGIwNmJjZjVkOWI1N2MxYWQgPSBMLnBvcHVwKHsibWF4V2lkdGgiOiAiMTAwJSJ9KTsKCiAgICAgICAgCiAgICAgICAgICAgIHZhciBodG1sXzJiZGUwNTRlMDdhZTRjMjU4NDAyZWQ1NWJhOWE2ZDQ5ID0gJChgPGRpdiBpZD0iaHRtbF8yYmRlMDU0ZTA3YWU0YzI1ODQwMmVkNTViYTlhNmQ0OSIgc3R5bGU9IndpZHRoOiAxMDAuMCU7IGhlaWdodDogMTAwLjAlOyI+VW5pdmVyc2l0eSBvZiBUb3JvbnRvLCBIYXJib3JkIENsdXN0ZXIgMDwvZGl2PmApWzBdOwogICAgICAgICAgICBwb3B1cF80ZTM5M2JkOThmMWQ0ODFkYjA2YmNmNWQ5YjU3YzFhZC5zZXRDb250ZW50KGh0bWxfMmJkZTA1NGUwN2FlNGMyNTg0MDJlZDU1YmE5YTZkNDkpOwogICAgICAgIAoKICAgICAgICBjaXJjbGVfbWFya2VyXzU2MDdlMzBmN2Q1NjQ4MzhiYTNkMmRhYzNiMWJjOWQyLmJpbmRQb3B1cChwb3B1cF80ZTM5M2JkOThmMWQ0ODFkYjA2YmNmNWQ5YjU3YzFhZCkKICAgICAgICA7CgogICAgICAgIAogICAgCiAgICAKICAgICAgICAgICAgdmFyIGNpcmNsZV9tYXJrZXJfYTI4YzQ4ZGRlZWViNGJkYzg4MzFmZmZjY2EwYjAwNWYgPSBMLmNpcmNsZU1hcmtlcigKICAgICAgICAgICAgICAgIFs0My42NTMyMDU3LCAtNzkuNDAwMDQ5M10sCiAgICAgICAgICAgICAgICB7ImJ1YmJsaW5nTW91c2VFdmVudHMiOiB0cnVlLCAiY29sb3IiOiAiI2ZmMDAwMCIsICJkYXNoQXJyYXkiOiBudWxsLCAiZGFzaE9mZnNldCI6IG51bGwsICJmaWxsIjogdHJ1ZSwgImZpbGxDb2xvciI6ICIjZmYwMDAwIiwgImZpbGxPcGFjaXR5IjogMC43LCAiZmlsbFJ1bGUiOiAiZXZlbm9kZCIsICJsaW5lQ2FwIjogInJvdW5kIiwgImxpbmVKb2luIjogInJvdW5kIiwgIm9wYWNpdHkiOiAxLjAsICJyYWRpdXMiOiA1LCAic3Ryb2tlIjogdHJ1ZSwgIndlaWdodCI6IDN9CiAgICAgICAgICAgICkuYWRkVG8obWFwXzUyM2UwNWEyZjZlMTQ1MTZhNzI0NWM2YzkxM2VmYjA5KTsKICAgICAgICAKICAgIAogICAgICAgIHZhciBwb3B1cF9iNDFhMTE1ZDUxNTc0MTQ1YjAwOTEzNmJiYmFiMjhkMCA9IEwucG9wdXAoeyJtYXhXaWR0aCI6ICIxMDAlIn0pOwoKICAgICAgICAKICAgICAgICAgICAgdmFyIGh0bWxfMDEwNzY0ZjQ4MWU3NDYwMWI1YjNiMTJmOWVkNjYwZDkgPSAkKGA8ZGl2IGlkPSJodG1sXzAxMDc2NGY0ODFlNzQ2MDFiNWIzYjEyZjllZDY2MGQ5IiBzdHlsZT0id2lkdGg6IDEwMC4wJTsgaGVpZ2h0OiAxMDAuMCU7Ij5LZW5zaW5ndG9uIE1hcmtldCwgQ2hpbmF0b3duLCBHcmFuZ2UgUGFyayBDbHVzdGVyIDA8L2Rpdj5gKVswXTsKICAgICAgICAgICAgcG9wdXBfYjQxYTExNWQ1MTU3NDE0NWIwMDkxMzZiYmJhYjI4ZDAuc2V0Q29udGVudChodG1sXzAxMDc2NGY0ODFlNzQ2MDFiNWIzYjEyZjllZDY2MGQ5KTsKICAgICAgICAKCiAgICAgICAgY2lyY2xlX21hcmtlcl9hMjhjNDhkZGVlZWI0YmRjODgzMWZmZmNjYTBiMDA1Zi5iaW5kUG9wdXAocG9wdXBfYjQxYTExNWQ1MTU3NDE0NWIwMDkxMzZiYmJhYjI4ZDApCiAgICAgICAgOwoKICAgICAgICAKICAgIAogICAgCiAgICAgICAgICAgIHZhciBjaXJjbGVfbWFya2VyXzNjODE0NjQxMjBiNTQ1ZWU5YzBlMTI5NmZmZjc5NDQ4ID0gTC5jaXJjbGVNYXJrZXIoCiAgICAgICAgICAgICAgICBbNDMuNjI4OTQ2NywgLTc5LjM5NDQxOTldLAogICAgICAgICAgICAgICAgeyJidWJibGluZ01vdXNlRXZlbnRzIjogdHJ1ZSwgImNvbG9yIjogIiNmZjAwMDAiLCAiZGFzaEFycmF5IjogbnVsbCwgImRhc2hPZmZzZXQiOiBudWxsLCAiZmlsbCI6IHRydWUsICJmaWxsQ29sb3IiOiAiI2ZmMDAwMCIsICJmaWxsT3BhY2l0eSI6IDAuNywgImZpbGxSdWxlIjogImV2ZW5vZGQiLCAibGluZUNhcCI6ICJyb3VuZCIsICJsaW5lSm9pbiI6ICJyb3VuZCIsICJvcGFjaXR5IjogMS4wLCAicmFkaXVzIjogNSwgInN0cm9rZSI6IHRydWUsICJ3ZWlnaHQiOiAzfQogICAgICAgICAgICApLmFkZFRvKG1hcF81MjNlMDVhMmY2ZTE0NTE2YTcyNDVjNmM5MTNlZmIwOSk7CiAgICAgICAgCiAgICAKICAgICAgICB2YXIgcG9wdXBfNTg2NjBlOGMzNGY4NDM3Nzk5MzY0NzQ4Mjc1OTI1NWMgPSBMLnBvcHVwKHsibWF4V2lkdGgiOiAiMTAwJSJ9KTsKCiAgICAgICAgCiAgICAgICAgICAgIHZhciBodG1sXzRkY2JmNTExN2ZkNzQ0YTJhY2I5NjM0NTRhZjdlNDk0ID0gJChgPGRpdiBpZD0iaHRtbF80ZGNiZjUxMTdmZDc0NGEyYWNiOTYzNDU0YWY3ZTQ5NCIgc3R5bGU9IndpZHRoOiAxMDAuMCU7IGhlaWdodDogMTAwLjAlOyI+Q04gVG93ZXIsIEtpbmcgYW5kIFNwYWRpbmEsIFJhaWx3YXkgTGFuZHMsIEhhcmJvdXJmcm9udCBXZXN0LCBCYXRodXJzdCBRdWF5LCBTb3V0aCBOaWFnYXJhLCBJc2xhbmQgYWlycG9ydCBDbHVzdGVyIDA8L2Rpdj5gKVswXTsKICAgICAgICAgICAgcG9wdXBfNTg2NjBlOGMzNGY4NDM3Nzk5MzY0NzQ4Mjc1OTI1NWMuc2V0Q29udGVudChodG1sXzRkY2JmNTExN2ZkNzQ0YTJhY2I5NjM0NTRhZjdlNDk0KTsKICAgICAgICAKCiAgICAgICAgY2lyY2xlX21hcmtlcl8zYzgxNDY0MTIwYjU0NWVlOWMwZTEyOTZmZmY3OTQ0OC5iaW5kUG9wdXAocG9wdXBfNTg2NjBlOGMzNGY4NDM3Nzk5MzY0NzQ4Mjc1OTI1NWMpCiAgICAgICAgOwoKICAgICAgICAKICAgIAogICAgCiAgICAgICAgICAgIHZhciBjaXJjbGVfbWFya2VyXzg4YTVkMWM4YTI0YjQ3ZTI4MDY2OTEyNTgzYzdjMTk2ID0gTC5jaXJjbGVNYXJrZXIoCiAgICAgICAgICAgICAgICBbNDMuNjQ2NDM1MiwgLTc5LjM3NDg0NTk5OTk5OTk5XSwKICAgICAgICAgICAgICAgIHsiYnViYmxpbmdNb3VzZUV2ZW50cyI6IHRydWUsICJjb2xvciI6ICIjZmYwMDAwIiwgImRhc2hBcnJheSI6IG51bGwsICJkYXNoT2Zmc2V0IjogbnVsbCwgImZpbGwiOiB0cnVlLCAiZmlsbENvbG9yIjogIiNmZjAwMDAiLCAiZmlsbE9wYWNpdHkiOiAwLjcsICJmaWxsUnVsZSI6ICJldmVub2RkIiwgImxpbmVDYXAiOiAicm91bmQiLCAibGluZUpvaW4iOiAicm91bmQiLCAib3BhY2l0eSI6IDEuMCwgInJhZGl1cyI6IDUsICJzdHJva2UiOiB0cnVlLCAid2VpZ2h0IjogM30KICAgICAgICAgICAgKS5hZGRUbyhtYXBfNTIzZTA1YTJmNmUxNDUxNmE3MjQ1YzZjOTEzZWZiMDkpOwogICAgICAgIAogICAgCiAgICAgICAgdmFyIHBvcHVwXzM2ZmY1NTA3OGI1NDRlY2ZhNmQzMDM3MGVkZTQ0Njk2ID0gTC5wb3B1cCh7Im1heFdpZHRoIjogIjEwMCUifSk7CgogICAgICAgIAogICAgICAgICAgICB2YXIgaHRtbF85NjkyNmMzM2MyODE0MDM1YmI1MzVlOWFkNDhhZTdmMiA9ICQoYDxkaXYgaWQ9Imh0bWxfOTY5MjZjMzNjMjgxNDAzNWJiNTM1ZTlhZDQ4YWU3ZjIiIHN0eWxlPSJ3aWR0aDogMTAwLjAlOyBoZWlnaHQ6IDEwMC4wJTsiPlN0biBBIFBPIEJveGVzIENsdXN0ZXIgMDwvZGl2PmApWzBdOwogICAgICAgICAgICBwb3B1cF8zNmZmNTUwNzhiNTQ0ZWNmYTZkMzAzNzBlZGU0NDY5Ni5zZXRDb250ZW50KGh0bWxfOTY5MjZjMzNjMjgxNDAzNWJiNTM1ZTlhZDQ4YWU3ZjIpOwogICAgICAgIAoKICAgICAgICBjaXJjbGVfbWFya2VyXzg4YTVkMWM4YTI0YjQ3ZTI4MDY2OTEyNTgzYzdjMTk2LmJpbmRQb3B1cChwb3B1cF8zNmZmNTUwNzhiNTQ0ZWNmYTZkMzAzNzBlZGU0NDY5NikKICAgICAgICA7CgogICAgICAgIAogICAgCiAgICAKICAgICAgICAgICAgdmFyIGNpcmNsZV9tYXJrZXJfMjYwZDE4OGJjOTFlNGI2NmI5ZjQ1ZmVkNDViMzc5NDcgPSBMLmNpcmNsZU1hcmtlcigKICAgICAgICAgICAgICAgIFs0My42NDg0MjkyLCAtNzkuMzgyMjgwMl0sCiAgICAgICAgICAgICAgICB7ImJ1YmJsaW5nTW91c2VFdmVudHMiOiB0cnVlLCAiY29sb3IiOiAiI2ZmMDAwMCIsICJkYXNoQXJyYXkiOiBudWxsLCAiZGFzaE9mZnNldCI6IG51bGwsICJmaWxsIjogdHJ1ZSwgImZpbGxDb2xvciI6ICIjZmYwMDAwIiwgImZpbGxPcGFjaXR5IjogMC43LCAiZmlsbFJ1bGUiOiAiZXZlbm9kZCIsICJsaW5lQ2FwIjogInJvdW5kIiwgImxpbmVKb2luIjogInJvdW5kIiwgIm9wYWNpdHkiOiAxLjAsICJyYWRpdXMiOiA1LCAic3Ryb2tlIjogdHJ1ZSwgIndlaWdodCI6IDN9CiAgICAgICAgICAgICkuYWRkVG8obWFwXzUyM2UwNWEyZjZlMTQ1MTZhNzI0NWM2YzkxM2VmYjA5KTsKICAgICAgICAKICAgIAogICAgICAgIHZhciBwb3B1cF83NzA3ZDg0MWVhZmQ0M2VjYWJjYTIxZDQ3OTQzMjhhMiA9IEwucG9wdXAoeyJtYXhXaWR0aCI6ICIxMDAlIn0pOwoKICAgICAgICAKICAgICAgICAgICAgdmFyIGh0bWxfN2I2ZmM3ZDJhYjJlNGE4NmE4M2FiMjZiZjc3MmVhNWMgPSAkKGA8ZGl2IGlkPSJodG1sXzdiNmZjN2QyYWIyZTRhODZhODNhYjI2YmY3NzJlYTVjIiBzdHlsZT0id2lkdGg6IDEwMC4wJTsgaGVpZ2h0OiAxMDAuMCU7Ij5GaXJzdCBDYW5hZGlhbiBQbGFjZSwgVW5kZXJncm91bmQgY2l0eSBDbHVzdGVyIDA8L2Rpdj5gKVswXTsKICAgICAgICAgICAgcG9wdXBfNzcwN2Q4NDFlYWZkNDNlY2FiY2EyMWQ0Nzk0MzI4YTIuc2V0Q29udGVudChodG1sXzdiNmZjN2QyYWIyZTRhODZhODNhYjI2YmY3NzJlYTVjKTsKICAgICAgICAKCiAgICAgICAgY2lyY2xlX21hcmtlcl8yNjBkMTg4YmM5MWU0YjY2YjlmNDVmZWQ0NWIzNzk0Ny5iaW5kUG9wdXAocG9wdXBfNzcwN2Q4NDFlYWZkNDNlY2FiY2EyMWQ0Nzk0MzI4YTIpCiAgICAgICAgOwoKICAgICAgICAKICAgIAogICAgCiAgICAgICAgICAgIHZhciBjaXJjbGVfbWFya2VyXzAyODdhMGViMWU2ZTQxMWY5ZmMyMDZmNDZkMDU3M2NhID0gTC5jaXJjbGVNYXJrZXIoCiAgICAgICAgICAgICAgICBbNDMuNjY5NTQyLCAtNzkuNDIyNTYzN10sCiAgICAgICAgICAgICAgICB7ImJ1YmJsaW5nTW91c2VFdmVudHMiOiB0cnVlLCAiY29sb3IiOiAiI2ZmMDAwMCIsICJkYXNoQXJyYXkiOiBudWxsLCAiZGFzaE9mZnNldCI6IG51bGwsICJmaWxsIjogdHJ1ZSwgImZpbGxDb2xvciI6ICIjZmYwMDAwIiwgImZpbGxPcGFjaXR5IjogMC43LCAiZmlsbFJ1bGUiOiAiZXZlbm9kZCIsICJsaW5lQ2FwIjogInJvdW5kIiwgImxpbmVKb2luIjogInJvdW5kIiwgIm9wYWNpdHkiOiAxLjAsICJyYWRpdXMiOiA1LCAic3Ryb2tlIjogdHJ1ZSwgIndlaWdodCI6IDN9CiAgICAgICAgICAgICkuYWRkVG8obWFwXzUyM2UwNWEyZjZlMTQ1MTZhNzI0NWM2YzkxM2VmYjA5KTsKICAgICAgICAKICAgIAogICAgICAgIHZhciBwb3B1cF8zZWQxZGQ2MTFjNDI0ODcxOWI3OTUyZTdlOTY3MzI3YiA9IEwucG9wdXAoeyJtYXhXaWR0aCI6ICIxMDAlIn0pOwoKICAgICAgICAKICAgICAgICAgICAgdmFyIGh0bWxfMTQ2YTRjYTFiYjcwNDE5ZWFhZTM2YmYyMDQ3NGE5OTMgPSAkKGA8ZGl2IGlkPSJodG1sXzE0NmE0Y2ExYmI3MDQxOWVhYWUzNmJmMjA0NzRhOTkzIiBzdHlsZT0id2lkdGg6IDEwMC4wJTsgaGVpZ2h0OiAxMDAuMCU7Ij5DaHJpc3RpZSBDbHVzdGVyIDA8L2Rpdj5gKVswXTsKICAgICAgICAgICAgcG9wdXBfM2VkMWRkNjExYzQyNDg3MTliNzk1MmU3ZTk2NzMyN2Iuc2V0Q29udGVudChodG1sXzE0NmE0Y2ExYmI3MDQxOWVhYWUzNmJmMjA0NzRhOTkzKTsKICAgICAgICAKCiAgICAgICAgY2lyY2xlX21hcmtlcl8wMjg3YTBlYjFlNmU0MTFmOWZjMjA2ZjQ2ZDA1NzNjYS5iaW5kUG9wdXAocG9wdXBfM2VkMWRkNjExYzQyNDg3MTliNzk1MmU3ZTk2NzMyN2IpCiAgICAgICAgOwoKICAgICAgICAKICAgIAogICAgCiAgICAgICAgICAgIHZhciBjaXJjbGVfbWFya2VyXzdhN2MzNzYwNjYxNjQ1MjVhYzZlZDQzYTdjMjFmYTgwID0gTC5jaXJjbGVNYXJrZXIoCiAgICAgICAgICAgICAgICBbNDMuNjY5MDA1MTAwMDAwMDEsIC03OS40NDIyNTkzXSwKICAgICAgICAgICAgICAgIHsiYnViYmxpbmdNb3VzZUV2ZW50cyI6IHRydWUsICJjb2xvciI6ICIjZmYwMDAwIiwgImRhc2hBcnJheSI6IG51bGwsICJkYXNoT2Zmc2V0IjogbnVsbCwgImZpbGwiOiB0cnVlLCAiZmlsbENvbG9yIjogIiNmZjAwMDAiLCAiZmlsbE9wYWNpdHkiOiAwLjcsICJmaWxsUnVsZSI6ICJldmVub2RkIiwgImxpbmVDYXAiOiAicm91bmQiLCAibGluZUpvaW4iOiAicm91bmQiLCAib3BhY2l0eSI6IDEuMCwgInJhZGl1cyI6IDUsICJzdHJva2UiOiB0cnVlLCAid2VpZ2h0IjogM30KICAgICAgICAgICAgKS5hZGRUbyhtYXBfNTIzZTA1YTJmNmUxNDUxNmE3MjQ1YzZjOTEzZWZiMDkpOwogICAgICAgIAogICAgCiAgICAgICAgdmFyIHBvcHVwXzM5MWRmMDdmOTRmZDQ2YjdhMjdkMzExNmVkNTI5NzA2ID0gTC5wb3B1cCh7Im1heFdpZHRoIjogIjEwMCUifSk7CgogICAgICAgIAogICAgICAgICAgICB2YXIgaHRtbF85NGExYWZiMjVhMzU0MTJiYmMwODVlODM3NzRhYzA0MiA9ICQoYDxkaXYgaWQ9Imh0bWxfOTRhMWFmYjI1YTM1NDEyYmJjMDg1ZTgzNzc0YWMwNDIiIHN0eWxlPSJ3aWR0aDogMTAwLjAlOyBoZWlnaHQ6IDEwMC4wJTsiPkR1ZmZlcmluLCBEb3ZlcmNvdXJ0IFZpbGxhZ2UgQ2x1c3RlciAwPC9kaXY+YClbMF07CiAgICAgICAgICAgIHBvcHVwXzM5MWRmMDdmOTRmZDQ2YjdhMjdkMzExNmVkNTI5NzA2LnNldENvbnRlbnQoaHRtbF85NGExYWZiMjVhMzU0MTJiYmMwODVlODM3NzRhYzA0Mik7CiAgICAgICAgCgogICAgICAgIGNpcmNsZV9tYXJrZXJfN2E3YzM3NjA2NjE2NDUyNWFjNmVkNDNhN2MyMWZhODAuYmluZFBvcHVwKHBvcHVwXzM5MWRmMDdmOTRmZDQ2YjdhMjdkMzExNmVkNTI5NzA2KQogICAgICAgIDsKCiAgICAgICAgCiAgICAKICAgIAogICAgICAgICAgICB2YXIgY2lyY2xlX21hcmtlcl8yMjZjMmQ3ZGQxY2M0MzRiOTc5NDZhMTUwYTUzYWIyNyA9IEwuY2lyY2xlTWFya2VyKAogICAgICAgICAgICAgICAgWzQzLjY0NzkyNjcwMDAwMDAwNiwgLTc5LjQxOTc0OTddLAogICAgICAgICAgICAgICAgeyJidWJibGluZ01vdXNlRXZlbnRzIjogdHJ1ZSwgImNvbG9yIjogIiNmZjAwMDAiLCAiZGFzaEFycmF5IjogbnVsbCwgImRhc2hPZmZzZXQiOiBudWxsLCAiZmlsbCI6IHRydWUsICJmaWxsQ29sb3IiOiAiI2ZmMDAwMCIsICJmaWxsT3BhY2l0eSI6IDAuNywgImZpbGxSdWxlIjogImV2ZW5vZGQiLCAibGluZUNhcCI6ICJyb3VuZCIsICJsaW5lSm9pbiI6ICJyb3VuZCIsICJvcGFjaXR5IjogMS4wLCAicmFkaXVzIjogNSwgInN0cm9rZSI6IHRydWUsICJ3ZWlnaHQiOiAzfQogICAgICAgICAgICApLmFkZFRvKG1hcF81MjNlMDVhMmY2ZTE0NTE2YTcyNDVjNmM5MTNlZmIwOSk7CiAgICAgICAgCiAgICAKICAgICAgICB2YXIgcG9wdXBfYzI4MjIwYTk1NzBhNGMxN2FlZGE5NzNlZjk4YzEzMzcgPSBMLnBvcHVwKHsibWF4V2lkdGgiOiAiMTAwJSJ9KTsKCiAgICAgICAgCiAgICAgICAgICAgIHZhciBodG1sX2I0ZDE5ZjdhODZkMzQ2ZWRiZGFiMTRmNzJhZTBlOWMyID0gJChgPGRpdiBpZD0iaHRtbF9iNGQxOWY3YTg2ZDM0NmVkYmRhYjE0ZjcyYWUwZTljMiIgc3R5bGU9IndpZHRoOiAxMDAuMCU7IGhlaWdodDogMTAwLjAlOyI+TGl0dGxlIFBvcnR1Z2FsLCBUcmluaXR5IENsdXN0ZXIgMDwvZGl2PmApWzBdOwogICAgICAgICAgICBwb3B1cF9jMjgyMjBhOTU3MGE0YzE3YWVkYTk3M2VmOThjMTMzNy5zZXRDb250ZW50KGh0bWxfYjRkMTlmN2E4NmQzNDZlZGJkYWIxNGY3MmFlMGU5YzIpOwogICAgICAgIAoKICAgICAgICBjaXJjbGVfbWFya2VyXzIyNmMyZDdkZDFjYzQzNGI5Nzk0NmExNTBhNTNhYjI3LmJpbmRQb3B1cChwb3B1cF9jMjgyMjBhOTU3MGE0YzE3YWVkYTk3M2VmOThjMTMzNykKICAgICAgICA7CgogICAgICAgIAogICAgCiAgICAKICAgICAgICAgICAgdmFyIGNpcmNsZV9tYXJrZXJfZjVlY2I3MDc0YWQxNDhkZmJmMTA5M2FkNjY1ZTBlZmQgPSBMLmNpcmNsZU1hcmtlcigKICAgICAgICAgICAgICAgIFs0My42MzY4NDcyLCAtNzkuNDI4MTkxNDAwMDAwMDJdLAogICAgICAgICAgICAgICAgeyJidWJibGluZ01vdXNlRXZlbnRzIjogdHJ1ZSwgImNvbG9yIjogIiNmZjAwMDAiLCAiZGFzaEFycmF5IjogbnVsbCwgImRhc2hPZmZzZXQiOiBudWxsLCAiZmlsbCI6IHRydWUsICJmaWxsQ29sb3IiOiAiI2ZmMDAwMCIsICJmaWxsT3BhY2l0eSI6IDAuNywgImZpbGxSdWxlIjogImV2ZW5vZGQiLCAibGluZUNhcCI6ICJyb3VuZCIsICJsaW5lSm9pbiI6ICJyb3VuZCIsICJvcGFjaXR5IjogMS4wLCAicmFkaXVzIjogNSwgInN0cm9rZSI6IHRydWUsICJ3ZWlnaHQiOiAzfQogICAgICAgICAgICApLmFkZFRvKG1hcF81MjNlMDVhMmY2ZTE0NTE2YTcyNDVjNmM5MTNlZmIwOSk7CiAgICAgICAgCiAgICAKICAgICAgICB2YXIgcG9wdXBfYjBiZjUwNjJjODA3NDZlNjg5MWQ1M2E2MmQ0NTQwYTYgPSBMLnBvcHVwKHsibWF4V2lkdGgiOiAiMTAwJSJ9KTsKCiAgICAgICAgCiAgICAgICAgICAgIHZhciBodG1sXzNhYTE4Y2E5OGY5ZDRmNTE5ODMxYjU3ZWNlNWQwZDk2ID0gJChgPGRpdiBpZD0iaHRtbF8zYWExOGNhOThmOWQ0ZjUxOTgzMWI1N2VjZTVkMGQ5NiIgc3R5bGU9IndpZHRoOiAxMDAuMCU7IGhlaWdodDogMTAwLjAlOyI+QnJvY2t0b24sIFBhcmtkYWxlIFZpbGxhZ2UsIEV4aGliaXRpb24gUGxhY2UgQ2x1c3RlciAwPC9kaXY+YClbMF07CiAgICAgICAgICAgIHBvcHVwX2IwYmY1MDYyYzgwNzQ2ZTY4OTFkNTNhNjJkNDU0MGE2LnNldENvbnRlbnQoaHRtbF8zYWExOGNhOThmOWQ0ZjUxOTgzMWI1N2VjZTVkMGQ5Nik7CiAgICAgICAgCgogICAgICAgIGNpcmNsZV9tYXJrZXJfZjVlY2I3MDc0YWQxNDhkZmJmMTA5M2FkNjY1ZTBlZmQuYmluZFBvcHVwKHBvcHVwX2IwYmY1MDYyYzgwNzQ2ZTY4OTFkNTNhNjJkNDU0MGE2KQogICAgICAgIDsKCiAgICAgICAgCiAgICAKICAgIAogICAgICAgICAgICB2YXIgY2lyY2xlX21hcmtlcl9jZTc3ODE3ZDBkYTM0ZDE2YTExZDY0YTcxOTVjOWQ1OSA9IEwuY2lyY2xlTWFya2VyKAogICAgICAgICAgICAgICAgWzQzLjY2MTYwODMsIC03OS40NjQ3NjMyOTk5OTk5OV0sCiAgICAgICAgICAgICAgICB7ImJ1YmJsaW5nTW91c2VFdmVudHMiOiB0cnVlLCAiY29sb3IiOiAiI2ZmMDAwMCIsICJkYXNoQXJyYXkiOiBudWxsLCAiZGFzaE9mZnNldCI6IG51bGwsICJmaWxsIjogdHJ1ZSwgImZpbGxDb2xvciI6ICIjZmYwMDAwIiwgImZpbGxPcGFjaXR5IjogMC43LCAiZmlsbFJ1bGUiOiAiZXZlbm9kZCIsICJsaW5lQ2FwIjogInJvdW5kIiwgImxpbmVKb2luIjogInJvdW5kIiwgIm9wYWNpdHkiOiAxLjAsICJyYWRpdXMiOiA1LCAic3Ryb2tlIjogdHJ1ZSwgIndlaWdodCI6IDN9CiAgICAgICAgICAgICkuYWRkVG8obWFwXzUyM2UwNWEyZjZlMTQ1MTZhNzI0NWM2YzkxM2VmYjA5KTsKICAgICAgICAKICAgIAogICAgICAgIHZhciBwb3B1cF8xYjYwYjdlNDliMGI0Y2ZjODg2MjcyMGM4YTk5Yzc2OSA9IEwucG9wdXAoeyJtYXhXaWR0aCI6ICIxMDAlIn0pOwoKICAgICAgICAKICAgICAgICAgICAgdmFyIGh0bWxfZDUzMmRiZjcwYmIxNDYxM2FkZWIyMDYzZGE5NWNlM2QgPSAkKGA8ZGl2IGlkPSJodG1sX2Q1MzJkYmY3MGJiMTQ2MTNhZGViMjA2M2RhOTVjZTNkIiBzdHlsZT0id2lkdGg6IDEwMC4wJTsgaGVpZ2h0OiAxMDAuMCU7Ij5IaWdoIFBhcmssIFRoZSBKdW5jdGlvbiBTb3V0aCBDbHVzdGVyIDA8L2Rpdj5gKVswXTsKICAgICAgICAgICAgcG9wdXBfMWI2MGI3ZTQ5YjBiNGNmYzg4NjI3MjBjOGE5OWM3Njkuc2V0Q29udGVudChodG1sX2Q1MzJkYmY3MGJiMTQ2MTNhZGViMjA2M2RhOTVjZTNkKTsKICAgICAgICAKCiAgICAgICAgY2lyY2xlX21hcmtlcl9jZTc3ODE3ZDBkYTM0ZDE2YTExZDY0YTcxOTVjOWQ1OS5iaW5kUG9wdXAocG9wdXBfMWI2MGI3ZTQ5YjBiNGNmYzg4NjI3MjBjOGE5OWM3NjkpCiAgICAgICAgOwoKICAgICAgICAKICAgIAogICAgCiAgICAgICAgICAgIHZhciBjaXJjbGVfbWFya2VyX2FiNWQxNGU3NGY2ZTQ2ZTJhMjI2MTE2ZTM4YTc5NmYyID0gTC5jaXJjbGVNYXJrZXIoCiAgICAgICAgICAgICAgICBbNDMuNjQ4OTU5NywgLTc5LjQ1NjMyNV0sCiAgICAgICAgICAgICAgICB7ImJ1YmJsaW5nTW91c2VFdmVudHMiOiB0cnVlLCAiY29sb3IiOiAiI2ZmMDAwMCIsICJkYXNoQXJyYXkiOiBudWxsLCAiZGFzaE9mZnNldCI6IG51bGwsICJmaWxsIjogdHJ1ZSwgImZpbGxDb2xvciI6ICIjZmYwMDAwIiwgImZpbGxPcGFjaXR5IjogMC43LCAiZmlsbFJ1bGUiOiAiZXZlbm9kZCIsICJsaW5lQ2FwIjogInJvdW5kIiwgImxpbmVKb2luIjogInJvdW5kIiwgIm9wYWNpdHkiOiAxLjAsICJyYWRpdXMiOiA1LCAic3Ryb2tlIjogdHJ1ZSwgIndlaWdodCI6IDN9CiAgICAgICAgICAgICkuYWRkVG8obWFwXzUyM2UwNWEyZjZlMTQ1MTZhNzI0NWM2YzkxM2VmYjA5KTsKICAgICAgICAKICAgIAogICAgICAgIHZhciBwb3B1cF80MTQ5OWU1MzI0ZTg0MWNlYTYzMWE1ZThhOTM4ZmYxZiA9IEwucG9wdXAoeyJtYXhXaWR0aCI6ICIxMDAlIn0pOwoKICAgICAgICAKICAgICAgICAgICAgdmFyIGh0bWxfYTk5YTE5MDgwZWY3NDdlYjgzMTE0YjIxNzUzZTZhMDQgPSAkKGA8ZGl2IGlkPSJodG1sX2E5OWExOTA4MGVmNzQ3ZWI4MzExNGIyMTc1M2U2YTA0IiBzdHlsZT0id2lkdGg6IDEwMC4wJTsgaGVpZ2h0OiAxMDAuMCU7Ij5QYXJrZGFsZSwgUm9uY2VzdmFsbGVzIENsdXN0ZXIgMDwvZGl2PmApWzBdOwogICAgICAgICAgICBwb3B1cF80MTQ5OWU1MzI0ZTg0MWNlYTYzMWE1ZThhOTM4ZmYxZi5zZXRDb250ZW50KGh0bWxfYTk5YTE5MDgwZWY3NDdlYjgzMTE0YjIxNzUzZTZhMDQpOwogICAgICAgIAoKICAgICAgICBjaXJjbGVfbWFya2VyX2FiNWQxNGU3NGY2ZTQ2ZTJhMjI2MTE2ZTM4YTc5NmYyLmJpbmRQb3B1cChwb3B1cF80MTQ5OWU1MzI0ZTg0MWNlYTYzMWE1ZThhOTM4ZmYxZikKICAgICAgICA7CgogICAgICAgIAogICAgCiAgICAKICAgICAgICAgICAgdmFyIGNpcmNsZV9tYXJrZXJfNWYyODEyNDc5NmQ1NDIzYzkzODcxNzQ1YWM0MTdiY2EgPSBMLmNpcmNsZU1hcmtlcigKICAgICAgICAgICAgICAgIFs0My42NTE1NzA2LCAtNzkuNDg0NDQ5OV0sCiAgICAgICAgICAgICAgICB7ImJ1YmJsaW5nTW91c2VFdmVudHMiOiB0cnVlLCAiY29sb3IiOiAiI2ZmMDAwMCIsICJkYXNoQXJyYXkiOiBudWxsLCAiZGFzaE9mZnNldCI6IG51bGwsICJmaWxsIjogdHJ1ZSwgImZpbGxDb2xvciI6ICIjZmYwMDAwIiwgImZpbGxPcGFjaXR5IjogMC43LCAiZmlsbFJ1bGUiOiAiZXZlbm9kZCIsICJsaW5lQ2FwIjogInJvdW5kIiwgImxpbmVKb2luIjogInJvdW5kIiwgIm9wYWNpdHkiOiAxLjAsICJyYWRpdXMiOiA1LCAic3Ryb2tlIjogdHJ1ZSwgIndlaWdodCI6IDN9CiAgICAgICAgICAgICkuYWRkVG8obWFwXzUyM2UwNWEyZjZlMTQ1MTZhNzI0NWM2YzkxM2VmYjA5KTsKICAgICAgICAKICAgIAogICAgICAgIHZhciBwb3B1cF9iNmM4NzBiM2VjNmQ0NmVmYjViMmNjZTNmMTlkMmQ0YiA9IEwucG9wdXAoeyJtYXhXaWR0aCI6ICIxMDAlIn0pOwoKICAgICAgICAKICAgICAgICAgICAgdmFyIGh0bWxfNzMxNmYzZjEyNTc2NDNkZmFkMDUyYTk1YzM3OTQyZmIgPSAkKGA8ZGl2IGlkPSJodG1sXzczMTZmM2YxMjU3NjQzZGZhZDA1MmE5NWMzNzk0MmZiIiBzdHlsZT0id2lkdGg6IDEwMC4wJTsgaGVpZ2h0OiAxMDAuMCU7Ij5SdW5ueW1lZGUsIFN3YW5zZWEgQ2x1c3RlciAwPC9kaXY+YClbMF07CiAgICAgICAgICAgIHBvcHVwX2I2Yzg3MGIzZWM2ZDQ2ZWZiNWIyY2NlM2YxOWQyZDRiLnNldENvbnRlbnQoaHRtbF83MzE2ZjNmMTI1NzY0M2RmYWQwNTJhOTVjMzc5NDJmYik7CiAgICAgICAgCgogICAgICAgIGNpcmNsZV9tYXJrZXJfNWYyODEyNDc5NmQ1NDIzYzkzODcxNzQ1YWM0MTdiY2EuYmluZFBvcHVwKHBvcHVwX2I2Yzg3MGIzZWM2ZDQ2ZWZiNWIyY2NlM2YxOWQyZDRiKQogICAgICAgIDsKCiAgICAgICAgCiAgICAKICAgIAogICAgICAgICAgICB2YXIgY2lyY2xlX21hcmtlcl85MzI2Y2EyNzM1Yzg0MWEwYTk4NTMyNTIxYjY3MTM2MSA9IEwuY2lyY2xlTWFya2VyKAogICAgICAgICAgICAgICAgWzQzLjY2MjMwMTUsIC03OS4zODk0OTM4XSwKICAgICAgICAgICAgICAgIHsiYnViYmxpbmdNb3VzZUV2ZW50cyI6IHRydWUsICJjb2xvciI6ICIjZmYwMDAwIiwgImRhc2hBcnJheSI6IG51bGwsICJkYXNoT2Zmc2V0IjogbnVsbCwgImZpbGwiOiB0cnVlLCAiZmlsbENvbG9yIjogIiNmZjAwMDAiLCAiZmlsbE9wYWNpdHkiOiAwLjcsICJmaWxsUnVsZSI6ICJldmVub2RkIiwgImxpbmVDYXAiOiAicm91bmQiLCAibGluZUpvaW4iOiAicm91bmQiLCAib3BhY2l0eSI6IDEuMCwgInJhZGl1cyI6IDUsICJzdHJva2UiOiB0cnVlLCAid2VpZ2h0IjogM30KICAgICAgICAgICAgKS5hZGRUbyhtYXBfNTIzZTA1YTJmNmUxNDUxNmE3MjQ1YzZjOTEzZWZiMDkpOwogICAgICAgIAogICAgCiAgICAgICAgdmFyIHBvcHVwXzQ2YzQ3OWUxNDY2YjRmYThhYWFiNTE4N2E0ODM2MjE3ID0gTC5wb3B1cCh7Im1heFdpZHRoIjogIjEwMCUifSk7CgogICAgICAgIAogICAgICAgICAgICB2YXIgaHRtbF84OGI0ODQ1Y2U3YTA0NmY3YTkxM2M4ZmNiNmJlNGIwNSA9ICQoYDxkaXYgaWQ9Imh0bWxfODhiNDg0NWNlN2EwNDZmN2E5MTNjOGZjYjZiZTRiMDUiIHN0eWxlPSJ3aWR0aDogMTAwLjAlOyBoZWlnaHQ6IDEwMC4wJTsiPlF1ZWVuJiMzOTtzIFBhcmssIE9udGFyaW8gUHJvdmluY2lhbCBHb3Zlcm5tZW50IENsdXN0ZXIgMDwvZGl2PmApWzBdOwogICAgICAgICAgICBwb3B1cF80NmM0NzllMTQ2NmI0ZmE4YWFhYjUxODdhNDgzNjIxNy5zZXRDb250ZW50KGh0bWxfODhiNDg0NWNlN2EwNDZmN2E5MTNjOGZjYjZiZTRiMDUpOwogICAgICAgIAoKICAgICAgICBjaXJjbGVfbWFya2VyXzkzMjZjYTI3MzVjODQxYTBhOTg1MzI1MjFiNjcxMzYxLmJpbmRQb3B1cChwb3B1cF80NmM0NzllMTQ2NmI0ZmE4YWFhYjUxODdhNDgzNjIxNykKICAgICAgICA7CgogICAgICAgIAogICAgCiAgICAKICAgICAgICAgICAgdmFyIGNpcmNsZV9tYXJrZXJfYzVhZmZkMDAxMWI5NGI0ODkzMmE3NmIyZDIxOWE2NTAgPSBMLmNpcmNsZU1hcmtlcigKICAgICAgICAgICAgICAgIFs0My42NjI3NDM5LCAtNzkuMzIxNTU4XSwKICAgICAgICAgICAgICAgIHsiYnViYmxpbmdNb3VzZUV2ZW50cyI6IHRydWUsICJjb2xvciI6ICIjZmYwMDAwIiwgImRhc2hBcnJheSI6IG51bGwsICJkYXNoT2Zmc2V0IjogbnVsbCwgImZpbGwiOiB0cnVlLCAiZmlsbENvbG9yIjogIiNmZjAwMDAiLCAiZmlsbE9wYWNpdHkiOiAwLjcsICJmaWxsUnVsZSI6ICJldmVub2RkIiwgImxpbmVDYXAiOiAicm91bmQiLCAibGluZUpvaW4iOiAicm91bmQiLCAib3BhY2l0eSI6IDEuMCwgInJhZGl1cyI6IDUsICJzdHJva2UiOiB0cnVlLCAid2VpZ2h0IjogM30KICAgICAgICAgICAgKS5hZGRUbyhtYXBfNTIzZTA1YTJmNmUxNDUxNmE3MjQ1YzZjOTEzZWZiMDkpOwogICAgICAgIAogICAgCiAgICAgICAgdmFyIHBvcHVwXzhmMzIzZWM5OGEwMjRiNjE5NWEwZTUyYjM1ODQyYzMzID0gTC5wb3B1cCh7Im1heFdpZHRoIjogIjEwMCUifSk7CgogICAgICAgIAogICAgICAgICAgICB2YXIgaHRtbF82NmRkYzM0MWQ2NjU0Nzg5YTcwMDQ1MDYxNThkNTkxMSA9ICQoYDxkaXYgaWQ9Imh0bWxfNjZkZGMzNDFkNjY1NDc4OWE3MDA0NTA2MTU4ZDU5MTEiIHN0eWxlPSJ3aWR0aDogMTAwLjAlOyBoZWlnaHQ6IDEwMC4wJTsiPkJ1c2luZXNzIHJlcGx5IG1haWwgUHJvY2Vzc2luZyBDZW50cmUsIFNvdXRoIENlbnRyYWwgTGV0dGVyIFByb2Nlc3NpbmcgUGxhbnQgVG9yb250byBDbHVzdGVyIDA8L2Rpdj5gKVswXTsKICAgICAgICAgICAgcG9wdXBfOGYzMjNlYzk4YTAyNGI2MTk1YTBlNTJiMzU4NDJjMzMuc2V0Q29udGVudChodG1sXzY2ZGRjMzQxZDY2NTQ3ODlhNzAwNDUwNjE1OGQ1OTExKTsKICAgICAgICAKCiAgICAgICAgY2lyY2xlX21hcmtlcl9jNWFmZmQwMDExYjk0YjQ4OTMyYTc2YjJkMjE5YTY1MC5iaW5kUG9wdXAocG9wdXBfOGYzMjNlYzk4YTAyNGI2MTk1YTBlNTJiMzU4NDJjMzMpCiAgICAgICAgOwoKICAgICAgICAKICAgIAo8L3NjcmlwdD4= onload=\"this.contentDocument.open();this.contentDocument.write(atob(this.getAttribute('data-html')));this.contentDocument.close();\" allowfullscreen webkitallowfullscreen mozallowfullscreen></iframe></div></div>"
},
"metadata": {}
}
]
},
{
"metadata": {},
"cell_type": "code",
"source": "",
"execution_count": null,
"outputs": []
}
],
"metadata": {
"kernelspec": {
"name": "python3",
"display_name": "Python 3.6",
"language": "python"
},
"language_info": {
"name": "python",
"version": "3.6.9",
"mimetype": "text/x-python",
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"pygments_lexer": "ipython3",
"nbconvert_exporter": "python",
"file_extension": ".py"
}
},
"nbformat": 4,
"nbformat_minor": 1
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment