In the last module we started building some visualizations in order to answer specific substantive questions. In this module we will look at generating more advanced graphics that have annotations, combine several graphics into one, build some interactive graphics, and also do some mapping.

1 Combining Plots with {patchwork}

Often you have to combine and place multiple graphics into a single canvas. There are a few ways to do this but the easiest way is that offered by the {patchwork} package. Let us use the diamonds data for this section, a data frame with 53940 rows and 10 variables:

Variable Description
price price in US dollars ($326–$18,823)
carat weight of the diamond (0.2–5.01)
cut quality of the cut (Fair, Good, Very Good, Premium, Ideal)
color diamond colour, from D (best) to J (worst)
clarity a measurement of how clear the diamond is (I1 (worst), SI2, SI1, VS2, VS1, VVS2, VVS1, IF (best))
x length in mm (0–10.74)
y width in mm (0–58.9)
z depth in mm (0–31.8)
depth total depth percentage = z / mean(x, y) = 2 * z / (x + y) (43–79)
table width of top of diamond relative to widest point (43–95)

1.1 The Basics

To combine multiple plots, we need to save each plot with a unique name. I am calling them p1, p2, etc.

Notice the default layout here: p1 + p2 + p3 gives us the plots all in a row. But you may have other plans, for example, to put the scatterplot in a row all its own.

Now we have p3 in the second row, all by itself. Note that this was achieved via the / operator. What if we used | instead?

You ended up with two columns, the first containing only p1 and the second containing p2 and p3. So make a note of the difference between | and /. For example, note the following setup:

What if we wanted to squeeze in the fourth plot?

1.3 Spacing and Sizing

We can also tweak the sizes of individual rows and columns, control the space between plots, and so on. First up, spacing the plots with plot_spacer()

Sizing the plots with relative sizes?

Alternatively, we could specify size with unit vectors, as shown below.

1.4 Moving Beyond the grid

We can use a layout design to get a little more flexibility but still retain full control over the result. Layout designs can be done in two ways so let us see the easiest route – as a text setup. “When using the textual representation it is your responsibility to make sure that each area is rectangular. The only exception is # which denotes empty areas and can thus be of any shape.”

The other path is using area() inside layout, as shown below.

Watch the specification here with wrap_plots()

2 Mapping

Maps are very powerful visualizations because they allow you to highlight and reflect patterns, clusters, with relative ease. For example, is poverty really higher in Appalachian counties? What about the percent of the population without health insurance? Literacy? Opioid deaths; do they follow transportation routes? What about COVID-19 cases? Maps to the rescue!

Building a map requires a few elements. First and foremost, you need some data to show on a map. Second, you need to have the geographic coordinates needed to build a map, basically the latitude and longitude of the geographies (states, cities, school districts, etc.) that you want to map. Third, you want a column that contains the names of the geographies you want to map, and these should be properly formatted (i.e., in titlecase) for displaying on the map.

Let us start by building a simple state map with the {urbnmapr} package. It comes with the necessary data for states and counties, respectively, and works well with {ggplot2}. Note the reliance on geom_polygon() now.

## Rows: 83,933
## Columns: 9
## $ long       <dbl> -88.47323, -88.46888, -88.46866, -88.45504, -88.45496, -88…
## $ lat        <dbl> 31.89386, 31.93026, 31.93317, 32.03972, 32.04058, 32.05305…
## $ order      <int> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,…
## $ hole       <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FA…
## $ piece      <fct> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1…
## $ group      <fct> 01.1, 01.1, 01.1, 01.1, 01.1, 01.1, 01.1, 01.1, 01.1, 01.1…
## $ state_fips <chr> "01", "01", "01", "01", "01", "01", "01", "01", "01", "01"…
## $ state_abbv <chr> "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL"…
## $ state_name <chr> "Alabama", "Alabama", "Alabama", "Alabama", "Alabama", "Al…
## Rows: 208,874
## Columns: 12
## $ long        <dbl> -86.91760, -86.81657, -86.71339, -86.71422, -86.41312, -8…
## $ lat         <dbl> 32.66417, 32.66012, 32.66173, 32.70569, 32.70739, 32.4099…
## $ order       <int> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17…
## $ hole        <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, F…
## $ piece       <fct> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, …
## $ group       <fct> 01001.1, 01001.1, 01001.1, 01001.1, 01001.1, 01001.1, 010…
## $ county_fips <chr> "01001", "01001", "01001", "01001", "01001", "01001", "01…
## $ state_abbv  <chr> "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL…
## $ state_fips  <chr> "01", "01", "01", "01", "01", "01", "01", "01", "01", "01…
## $ county_name <chr> "Autauga County", "Autauga County", "Autauga County", "Au…
## $ fips_class  <chr> "H1", "H1", "H1", "H1", "H1", "H1", "H1", "H1", "H1", "H1…
## $ state_name  <chr> "Alabama", "Alabama", "Alabama", "Alabama", "Alabama", "A…

Note that this is just an empty map with the shapes of the states, and also that Alaska and Hawaii have been moved so that they can be displayed on the map. We could build a much better map by removing the x and y axis labels and tick marks, and setting a white background using theme_map() from the {ggthemes} package. We could also fill with some colors, say on the basis of the state_name.

Okay, this is not very useful because it would be much better to color the map on the basis of some substantive variable. Let us see what lurks in the statedata file.

## Rows: 51
## Columns: 6
## $ year        <int> 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 201…
## $ state_fips  <chr> "01", "02", "04", "05", "06", "08", "09", "10", "11", "12…
## $ state_name  <chr> "Alabama", "Alaska", "Arizona", "Arkansas", "California",…
## $ hhpop       <int> 1846380, 250183, 2463012, 1144657, 12895471, 2074517, 134…
## $ horate      <dbl> 0.6814329, 0.6311860, 0.6206178, 0.6546031, 0.5372219, 0.…
## $ medhhincome <int> 44700, 70600, 51000, 42000, 64600, 63500, 71700, 61200, 7…

Okay, two things stand out – horate (the homeownership rate), and medhhincome (the median household income). Let us fill with median household income but to do so, we will need to join statedata to our states file. Why? Because we need coordinates to map anything and statedata does not contain coordinates. Then we can specify fill = medhhincome inside the aes(...) command.

## Rows: 83,933
## Columns: 13
## $ long        <dbl> -88.47323, -88.46888, -88.46866, -88.45504, -88.45496, -8…
## $ lat         <dbl> 31.89386, 31.93026, 31.93317, 32.03972, 32.04058, 32.0530…
## $ order       <int> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17…
## $ hole        <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, F…
## $ piece       <fct> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, …
## $ group       <fct> 01.1, 01.1, 01.1, 01.1, 01.1, 01.1, 01.1, 01.1, 01.1, 01.…
## $ state_fips  <chr> "01", "01", "01", "01", "01", "01", "01", "01", "01", "01…
## $ state_abbv  <chr> "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL…
## $ state_name  <chr> "Alabama", "Alabama", "Alabama", "Alabama", "Alabama", "A…
## $ year        <int> 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 201…
## $ hhpop       <int> 1846380, 1846380, 1846380, 1846380, 1846380, 1846380, 184…
## $ horate      <dbl> 0.6814329, 0.6814329, 0.6814329, 0.6814329, 0.6814329, 0.…
## $ medhhincome <int> 44700, 44700, 44700, 44700, 44700, 44700, 44700, 44700, 4…

What about working with counties? Sure, let us merge countydata with the `counties file and map.

Maybe you are only interested in Florida?

Hmm, so far so good but what if the data were for some geography not bundled with {urbnmapr}, school districts or places, for example? Not a problem, we just have to go the extra mile. First we would have to find, download, and upload the shapefile. Say I am looking for places (loosely described as municipalities) in New Hampshire. Well, the {tigris} package comes in handy because it allows you to get whatever geography’s shapefiles you want. Below I am getting the shapefile for New Hampshire.

Okay, so now that I have the shapefile, how can I use it? I need to fortify it so that it looks like a regular dataframe rather than the native SpatialPolygonsDataFrame format it comes in. When I go to make the map I am going to add the state shapefile too since otherwise the state’s boundary will not show up.

Of course, the fill is superficial here. But say we had some data for places in New Hampshire, maybe the size of the population, as in nh.data.RData. Now we could join nh.data with nh.df to create nh and then map.

## Rows: 3,939
## Columns: 9
## $ long       <dbl> -71.22967, -71.22858, -71.22532, -71.22349, -71.22136, -71…
## $ lat        <dbl> 43.46519, 43.46392, 43.46597, 43.46411, 43.46168, 43.46163…
## $ order      <int> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,…
## $ hole       <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FA…
## $ piece      <fct> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1…
## $ id         <chr> "3300980", "3300980", "3300980", "3300980", "3300980", "33…
## $ group      <fct> 3300980.1, 3300980.1, 3300980.1, 3300980.1, 3300980.1, 330…
## $ NAME       <chr> "Alton CDP, New Hampshire", "Alton CDP, New Hampshire", "A…
## $ population <dbl> 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168…

Now we plot:

You could have also filled by creating quartiles, etc., using {Santoku}, so do not forget that option.

Before we move on, one more map, to show you the possibilities. Here, I am plotting the locations of the parking tickets issued in Philadelphia, this time with the {leaflet} package. First the tickets data-set, reduced to a random sample of 20% of the tickets issued in the month of December in 2017 (to keep the data size manageable). I have also created a popup that will display specific information if someone clicks on a point in the map.

Voila! A few lines of code and we have an interactive map that can be used to display whatever evidence we want to display. Note that you need geographic coordinates since without them the data cannot be attached to a physical location.

Let us see another twist on this. Say I am trying to map the total number of COVID-19 cases in Ohio. I know the county and I know the number of cases that occurred, as well as the latitude and longitude of each county. Well, I can build a similar plot except making the size of the circle conditional upon the number of cases. The more the cases, the larger the radius of the circle.

But there are no coordinates in cov19. Well, I can get those from the {housingData} package, and then merge that with cov19.

## Rows: 88
## Columns: 14
## $ fips.x     <fct> 39001, 39003, 39005, 39007, 39009, 39011, 39013, 39015, 39…
## $ display    <chr> "Adams: 3", "Allen: 65", "Ashland: 5", "Ashtabula: 54", "A…
## $ countyname <chr> "Adams", "Allen", "Ashland", "Ashtabula", "Athens", "Augla…
## $ extra      <chr> "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""…
## $ state.x    <fct> OH, OH, OH, OH, OH, OH, OH, OH, OH, OH, OH, OH, OH, OH, OH…
## $ lon        <dbl> -83.46359, -84.10825, -82.26922, -80.75647, -82.04053, -84…
## $ lat        <dbl> 38.85662, 40.77675, 40.86122, 41.71017, 39.34576, 40.56421…
## $ rMapState  <fct> ohio, ohio, ohio, ohio, ohio, ohio, ohio, ohio, ohio, ohio…
## $ rMapCounty <fct> adams, allen, ashland, ashtabula, athens, auglaize, belmon…
## $ date       <date> 2020-04-17, 2020-04-17, 2020-04-17, 2020-04-17, 2020-04-1…
## $ state.y    <chr> "Ohio", "Ohio", "Ohio", "Ohio", "Ohio", "Ohio", "Ohio", "O…
## $ fips.y     <chr> "39001", "39003", "39005", "39007", "39009", "39011", "390…
## $ cases      <dbl> 3, 65, 5, 54, 3, 21, 63, 8, 155, 15, 6, 25, 66, 26, 154, 1…
## $ deaths     <dbl> 0, 9, 0, 4, 1, 1, 3, 1, 2, 0, 1, 0, 1, 0, 11, 0, 0, 48, 10…

Okay, now we have everything we need to build the map.

3 Interactive Graphics with Plotly and Highcharter

Interactive graphics are useful in situations where you would like the user/viewer to see the data values or other details. Say, for example, I have a plots and want to make it interactive. How can I do that? By saving my regular plot and then using {plotly} to add a ggplotly() wrapper around the plot.

These plots are useful when presenting data to a live audience (in a talk, or on the web). But, I prefer {highcharter} since it does a lot of things well, and they are aesthetically pleasing. Let us stay with the COVID-19 example. Say I want a bar-chart of the total number of cases by state.

Notice the key elements here: The basic function call is hchart() and we are specifying that we want a bar-chart, and we are also providing the quantities that should go on the x and y axis, respectively. Note that x actually ends up as the y when you specify a “bar” chart.

What if I wanted a line-chart, maybe of the number of cases over time? And I wanted this just for a few states? We could do that too, as shown below. Note that I am creating tab2, a frequency table of the number of cases by state and date, and then converting total_cases into a logarithmic form (saved as log_cases) so that we can compare the rate of change from one date to the next on a common scale.

Now here is a county-level chart that shows the total number of cases as of April 14, 2020. This is stored in tab3 created as shown below. Pay attention to this creation because we are not just creating a frequency table but also adding in a specific key we are calling code because we will need to join these data to the map data.

Here comes the map!

Note that countries/us/us-all-all indicates that we want counties. If we wanted the states instead it would have been countries/us/us-all. The value = flags the column that has to be used to populate the color scheme. The joinBy connects our data (tab4) to hcmap() so that total_cases gets attached to the correct location in the map.

What if we wanted only Ohio? Well, in that case we could subset as shown below:

4 Practice Exercises

Exercise (1)

Create a map of all the counties in New York. Be sure to title the map and to fill in each county with the total number of COVID19 cases they have seen to date. In addition, draw county borders in white. Use theme_map() and make sure the legend is at the bottom. [Hint: You will need to calculate the total number of cases per county and then join the resulting file with the counties data file to get the latitude/longitudes for the counties.]

Exercise (2)

Run the following code chunk to load data on the murder, assault and rape rates per 100,000 persons. Urbanpop is the percent of the state population that lives in an urban area.

## [1] "Murder"   "Assault"  "UrbanPop" "Rape"

Now create a state-level map of the 50 states making sure to use UrbanPop to fill each state. Title the map and place the legend at the bottom.

Exercise (3)

Use the USArrests data to draw scatterplots of (a) Murder versus UrbanPop, (b) Assault versus UrbanPop, and (c) Rape versus UrbanPop. Save each of these scatterplots by name and then use patchwork to create a single canvas that includes all three plots. Make sure you label the x-axis, y-axis, and title each plot.

Exercise (4)

Now create highcharter versions of each of the three scatterplots you created in Exercise (3) above. You should end up with three scatterplots, each on its own canvas.

Exercise (5)

The sftrees dataset includes various pieces of data on trees in San Francisco. In turn, sf.df is a 10% random sample of these trees.

Use leaflet to create a map that shows, as circles, the location of each tree, and make sure when someone clicks on the circle the popup shows the species. Also ensure that the circles are green in color.

LS0tCnRpdGxlOiAiTVBBIDU4MzAgLSBNb2R1bGUgMDYiCnN1YnRpdGxlOiAiU3ByaW5nIDIwMjAiCmF1dGhvcjogIlByb2Zlc3NvciBSdWhpbCIKZGF0ZTogIlVwZGF0ZWQgb24gYHIgU3lzLkRhdGUoKWAiCm91dHB1dDogCiAgaHRtbF9kb2N1bWVudDogCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMKICAgIGZpZ19jYXB0aW9uOiB5ZXMKICAgIGhpZ2hsaWdodDogemVuYnVybgogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMKICAgIHRoZW1lOiBmbGF0bHkKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwplZGl0b3Jfb3B0aW9uczogCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGNvbnNvbGUKLS0tCgo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPgoKYm9keXsgLyogTm9ybWFsICAqLwovKiAgICBmb250LWZhbWlseTogTGF0bywgc2Fucy1zZXJpZjsgIAogICAgICBmb250LWZhbWlseTogTXVrdGEsIHNhbnMtc2VyaWY7IAogICAgICBmb250LWZhbWlseTogJ051bml0byBTYW5zJywgc2Fucy1zZXJpZjsKICAgICAgZm9udC1mYW1pbHk6IEthcmxhLCBzYW5zLXNlcmlmOyAgKi8KICAgICAgZm9udC1mYW1pbHk6ICdNZXJyaXdlYXRoZXIgU2FucycsIHNhbnMtc2VyaWY7IAogICAgICBmb250LXNpemU6IDE4cHg7CiAgfQoKaDEudGl0bGUgewogIGZvbnQtc2l6ZTogMzhweDsKICBjb2xvcjogRGFya1JlZDsKfQoKaDEgeyAvKiBIZWFkZXIgMSAqLwogIGZvbnQtc2l6ZTogMjhweDsKICBjb2xvcjogRGFya0JsdWU7Cn0KCmgyIHsgLyogSGVhZGVyIDIgKi8KICAgIGZvbnQtc2l6ZTogMjJweDsKICBjb2xvcjogRGFya0JsdWU7Cn0KCmgzIHsgLyogSGVhZGVyIDMgKi8KICBmb250LXNpemU6IDE4cHg7CiAgY29sb3I6IERhcmtCbHVlOwp9Cgpjb2RlLnJ7IC8qIENvZGUgYmxvY2sgKi8KICAgIGZvbnQtZmFtaWx5OiBNdWt0YSwgc2Fucy1zZXJpZjsgCiAgICBmb250LXdlaWdodDogNjAwOyAgCiAgICBmb250LXNpemU6IDE2cHg7Cn0KCi8qIHByZSB7IC8qIENvZGUgYmxvY2sgLSBkZXRlcm1pbmVzIGNvZGUgc3BhY2luZyBiZXR3ZWVuIGxpbmVzICovCiAgICBmb250LXNpemU6IDE2cHg7Cn0gKi8KPC9zdHlsZT4KCgpgYGB7ciBrbGlwcHksIGVjaG8gPSBGQUxTRSwgaW5jbHVkZSA9IFRSVUV9CmtsaXBweTo6a2xpcHB5KHRvb2x0aXBfbWVzc2FnZSA9ICdDbGljayB0byBjb3B5JywgdG9vbHRpcF9zdWNjZXNzID0gJ0RvbmUnLCBjb2xvciA9ICdjb3JuZmxvd2VyYmx1ZScsIHBvc2l0aW9uID0gYygndG9wJywgJ3JpZ2h0JykpCmBgYAoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGRwaSA9IDEwMCwgY2FjaGUgPSBUUlVFLCBmaWcuYWxpZ24gPSAiY2VudGVyIiwgZmlnLndpZHRoID0gNywgZmlnLmhlaWdodCA9IDUsIG91dC53aWR0aCA9ICIxMDAlIiwgaGlnaGxpZ2h0ID0gVFJVRSkgCmBgYAoKSW4gdGhlIGxhc3QgbW9kdWxlIHdlIHN0YXJ0ZWQgYnVpbGRpbmcgc29tZSB2aXN1YWxpemF0aW9ucyBpbiBvcmRlciB0byBhbnN3ZXIgc3BlY2lmaWMgc3Vic3RhbnRpdmUgcXVlc3Rpb25zLiBJbiB0aGlzIG1vZHVsZSB3ZSB3aWxsIGxvb2sgYXQgZ2VuZXJhdGluZyBtb3JlIGFkdmFuY2VkIGdyYXBoaWNzIHRoYXQgaGF2ZSBhbm5vdGF0aW9ucywgY29tYmluZSBzZXZlcmFsIGdyYXBoaWNzIGludG8gb25lLCBidWlsZCBzb21lIGludGVyYWN0aXZlIGdyYXBoaWNzLCBhbmQgYWxzbyBkbyBzb21lIG1hcHBpbmcuIAoKIyBDb21iaW5pbmcgUGxvdHMgd2l0aCBge3BhdGNod29ya31gCk9mdGVuIHlvdSBoYXZlIHRvIGNvbWJpbmUgYW5kIHBsYWNlIG11bHRpcGxlIGdyYXBoaWNzIGludG8gYSBzaW5nbGUgY2FudmFzLiBUaGVyZSBhcmUgYSBmZXcgd2F5cyB0byBkbyB0aGlzIGJ1dCB0aGUgZWFzaWVzdCB3YXkgaXMgdGhhdCBvZmZlcmVkIGJ5IHRoZSBge3BhdGNod29ya31gIHBhY2thZ2UuIExldCB1cyB1c2UgdGhlIGBkaWFtb25kc2AgZGF0YSBmb3IgdGhpcyBzZWN0aW9uLCBhIGRhdGEgZnJhbWUgd2l0aCA1Mzk0MCByb3dzIGFuZCAxMCB2YXJpYWJsZXM6Cgp8IFZhcmlhYmxlIHwgRGVzY3JpcHRpb24gfAp8IDotLSB8IDotLSB8CnwgcHJpY2UgfCAgcHJpY2UgaW4gVVMgZG9sbGFycyAoXCQzMjYtLVwkMTgsODIzKSB8CnwgY2FyYXQgfCB3ZWlnaHQgb2YgdGhlIGRpYW1vbmQgKDAuMi0tNS4wMSkgfAp8IGN1dCB8IHF1YWxpdHkgb2YgdGhlIGN1dCAoRmFpciwgR29vZCwgVmVyeSBHb29kLCBQcmVtaXVtLCBJZGVhbCkgfAp8IGNvbG9yIHwgZGlhbW9uZCBjb2xvdXIsIGZyb20gRCAoYmVzdCkgdG8gSiAod29yc3QpIHwKfCBjbGFyaXR5IHwgYSBtZWFzdXJlbWVudCBvZiBob3cgY2xlYXIgdGhlIGRpYW1vbmQgaXMgKEkxICh3b3JzdCksIFNJMiwgU0kxLCBWUzIsIFZTMSwgVlZTMiwgVlZTMSwgSUYgKGJlc3QpKSB8CnwgeCB8IGxlbmd0aCBpbiBtbSAoMC0tMTAuNzQpIHwKfCB5IHwgd2lkdGggaW4gbW0gKDAtLTU4LjkpIHwKfCB6IHwgZGVwdGggaW4gbW0gKDAtLTMxLjgpIHwKfCBkZXB0aCB8IHRvdGFsIGRlcHRoIHBlcmNlbnRhZ2UgPSB6IC8gbWVhbih4LCB5KSA9IDIgKiB6IC8gKHggKyB5KSAoNDMtLTc5KSB8CnwgdGFibGUgfCB3aWR0aCBvZiB0b3Agb2YgZGlhbW9uZCByZWxhdGl2ZSB0byB3aWRlc3QgcG9pbnQgKDQzLS05NSkgfAoKCiMjIFRoZSBCYXNpY3MgClRvIGNvbWJpbmUgbXVsdGlwbGUgcGxvdHMsIHdlIG5lZWQgdG8gc2F2ZSBgZWFjaCBwbG90YCB3aXRoIGEgdW5pcXVlIG5hbWUuIEkgYW0gY2FsbGluZyB0aGVtIGBwMWAsIGBwMmAsIGV0Yy4gCgpgYGB7ciBwYXRjaDAxfQpsaWJyYXJ5KHBhdGNod29yaykKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkodGlkeWxvZykKCmdncGxvdCgpICsKICBnZW9tX2JhcihkYXRhID0gZGlhbW9uZHMsCiAgICAgICAgICAgYWVzKHggPSBjdXQsIGZpbGwgPSBjdXQpCiAgICAgICAgICAgKSArCiAgbGFicyh4ID0gIkN1dCBvZiB0aGUgRGlhbW9uZCIsIHkgPSAiRnJlcXVlbmN5IikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgLT4gcDEKCmdncGxvdCgpICsKICBnZW9tX2JhcihkYXRhID0gZGlhbW9uZHMsCiAgICAgICAgICAgYWVzKHggPSBjb2xvciwgZmlsbCA9IGNvbG9yKQogICAgICAgICAgICkgKwogIGxhYnMoeCA9ICJDb2xvciBvZiB0aGUgRGlhbW9uZCIsIHkgPSAiRnJlcXVlbmN5IikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgLT4gcDIKCmdncGxvdCgpICsKICBnZW9tX3BvaW50KGRhdGEgPSBkaWFtb25kcywKICAgICAgICAgICBhZXMoeCA9IGNhcmF0LCB5ID0gcHJpY2UsIGNvbG9yID0gY3V0KQogICAgICAgICAgICkgKwogIGxhYnMoeCA9ICJXZWlnaHQgb2YgdGhlIERpYW1vbmQiLCB5ID0gIlByaWNlIG9mIHRoZSBEaWFtb25kIiwgY29sb3IgPSAiIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSAtPiBwMwoKZ2dwbG90KCkgKwogIGdlb21fYm94cGxvdChkYXRhID0gZGlhbW9uZHMsCiAgICAgICAgICAgYWVzKHggPSBwcmljZSwgeSA9IGNsYXJpdHksIGZpbGwgPSBjdXQpCiAgICAgICAgICAgKSArCiAgbGFicyh5ID0gIkNsYXJpdHkgb2YgdGhlIERpYW1vbmQiLCB4ID0gIlByaWNlIG9mIHRoZSBEaWFtb25kIiwgZmlsbCA9ICIiKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpIC0+IHA0CgpwMSArIHAyICsgcDMKYGBgCgpOb3RpY2UgdGhlIGRlZmF1bHQgbGF5b3V0IGhlcmU6IGBwMSArIHAyICsgcDNgIGdpdmVzIHVzIHRoZSBwbG90cyBhbGwgaW4gYSByb3cuIEJ1dCB5b3UgbWF5IGhhdmUgb3RoZXIgcGxhbnMsIGZvciBleGFtcGxlLCB0byBwdXQgdGhlIHNjYXR0ZXJwbG90IGluIGEgcm93IGFsbCBpdHMgb3duLiAKCmBgYHtyIHBhdGNoMDJ9CihwMSArIHAyKSAvIHAzCmBgYAoKTm93IHdlIGhhdmUgYHAzYCBpbiB0aGUgc2Vjb25kIHJvdywgYWxsIGJ5IGl0c2VsZi4gTm90ZSB0aGF0IHRoaXMgd2FzIGFjaGlldmVkIHZpYSB0aGUgYC9gIG9wZXJhdG9yLiBXaGF0IGlmIHdlIHVzZWQgYHxgIGluc3RlYWQ/CgpgYGB7ciBwYXRjaDAzfQpwMSB8IChwMiArIHAzKQpgYGAKCllvdSBlbmRlZCB1cCB3aXRoIHR3byBjb2x1bW5zLCB0aGUgZmlyc3QgY29udGFpbmluZyBvbmx5IGBwMWAgYW5kIHRoZSBzZWNvbmQgY29udGFpbmluZyBgcDJgIGFuZCBgcDNgLiBTbyBtYWtlIGEgbm90ZSBvZiB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIGB8YCBhbmQgYC9gLiBGb3IgZXhhbXBsZSwgbm90ZSB0aGUgZm9sbG93aW5nIHNldHVwOgoKYGBge3IgcGF0Y2gwNH0KcDEgfCAocDIgLyBwMykKYGBgCgpXaGF0IGlmIHdlIHdhbnRlZCB0byBzcXVlZXplIGluIHRoZSBmb3VydGggcGxvdD8KCmBgYHtyIHBhdGNoMDV9CihwMSArIHAyKSAvIChwMyArIHA0KQpgYGAKCiMjIEFubm90YXRpb25zIApBbm5vdGF0aW9ucyBiZWNvbWUgaGVscGZ1bCBiZWNhdXNlIHlvdSBjYW4gYWRkIG9tbmlidXMgdGl0bGVzIGFuZCB0YWdzIGZvciBpbmRpdmlkdWFsIHBsb3RzLiBGb3IgZXhhbXBsZSwgCgpgYGB7ciBwYXRjaDA2fQoocDEgKyBwMikgLyAocDMgKyBwNCkgKwogIHBsb3RfYW5ub3RhdGlvbigKICB0aXRsZSA9ICdUaGUgc3VycHJpc2luZyB0cnV0aCBhYm91dCBkaWFtb25kcycsCiAgc3VidGl0bGUgPSAnVGhlc2UgcGxvdHMgd2lsbCByZXZlYWwgdW50b2xkIHNlY3JldHMgYWJvdXQgb25lIG9mIG91ciBiZWxvdmVkIGRhdGEtc2V0cycsCiAgY2FwdGlvbiA9ICdEaXNjbGFpbWVyOiBOb25lIG9mIHRoZXNlIHBsb3RzIGFyZSBpbnNpZ2h0ZnVsJywKICB0YWdfbGV2ZWxzID0gYygnYScsICcxJyksCiAgdGFnX3ByZWZpeCA9ICdGaWcuICcsCiAgdGFnX3NlcCA9ICcuJywKICB0YWdfc3VmZml4ID0gJzonCiAgKSAmCiAgdGhlbWUoCiAgICBwbG90LnRhZy5wb3NpdGlvbiA9IGMoMCwgMSksCiAgICBwbG90LnRhZyA9IGVsZW1lbnRfdGV4dChzaXplID0gOSwgaGp1c3QgPSAwLCB2anVzdCA9IDAsIGNvbG9yID0gInN0ZWVsYmx1ZSIpCiAgICApCmBgYAoKIyMgU3BhY2luZyBhbmQgU2l6aW5nCldlIGNhbiBhbHNvIHR3ZWFrIHRoZSBzaXplcyBvZiBpbmRpdmlkdWFsIHJvd3MgYW5kIGNvbHVtbnMsIGNvbnRyb2wgdGhlIHNwYWNlIGJldHdlZW4gcGxvdHMsIGFuZCBzbyBvbi4gRmlyc3QgdXAsIHNwYWNpbmcgdGhlIHBsb3RzIHdpdGggYHBsb3Rfc3BhY2VyKClgIAoKYGBge3IgcGF0Y2gwN30KKHAxICsgcGxvdF9zcGFjZXIoKSArIHAyICsgcGxvdF9zcGFjZXIoKSArIHAzKQpgYGAKCgpTaXppbmcgdGhlIHBsb3RzIHdpdGggcmVsYXRpdmUgc2l6ZXM/CgpgYGB7ciBwYXRjaDA4fQpwMSArIHAyICsgcDMgKyBwNCArIAogIHBsb3RfbGF5b3V0KHdpZHRocyA9IGMoMiwgMSkpCmBgYAoKQWx0ZXJuYXRpdmVseSwgd2UgY291bGQgc3BlY2lmeSBzaXplIHdpdGggdW5pdCB2ZWN0b3JzLCBhcyBzaG93biBiZWxvdy4gCgpgYGB7ciBwYXRjaDA5fQpwMSArIHAyICsgcDMgKyBwNCArIAogIHBsb3RfbGF5b3V0KAogICAgd2lkdGhzID0gYygyLCAxKSwKICAgIGhlaWdodHMgPSB1bml0KGMoNSwgMSksIGMoJ2NtJywgJ251bGwnKSkKICAgICkKYGBgCgojIyBNb3ZpbmcgQmV5b25kIHRoZSBncmlkCgpXZSBjYW4gdXNlIGEgYGxheW91dGAgZGVzaWduIHRvIGdldCBhIGxpdHRsZSBtb3JlIGZsZXhpYmlsaXR5IGJ1dCBzdGlsbCByZXRhaW4gZnVsbCBjb250cm9sIG92ZXIgdGhlIHJlc3VsdC4gTGF5b3V0IGRlc2lnbnMgY2FuIGJlIGRvbmUgaW4gdHdvIHdheXMgc28gbGV0IHVzIHNlZSB0aGUgZWFzaWVzdCByb3V0ZSAtLSBhcyBhIHRleHQgc2V0dXAuICJXaGVuIHVzaW5nIHRoZSB0ZXh0dWFsIHJlcHJlc2VudGF0aW9uIGl0IGlzIHlvdXIgcmVzcG9uc2liaWxpdHkgdG8gbWFrZSBzdXJlIHRoYXQgZWFjaCBhcmVhIGlzIHJlY3Rhbmd1bGFyLiBUaGUgb25seSBleGNlcHRpb24gaXMgIyB3aGljaCBkZW5vdGVzIGVtcHR5IGFyZWFzIGFuZCBjYW4gdGh1cyBiZSBvZiBhbnkgc2hhcGUuIgoKYGBge3IgcGF0Y2gxMH0KbGF5b3V0IDwtICIKIyNCQkJCCkFBQ0NERAojI0NDREQKIgpwMiArIHAzICsgcDQgKyBwMSArIAogIHBsb3RfbGF5b3V0KGRlc2lnbiA9IGxheW91dCkKYGBgCgpUaGUgb3RoZXIgcGF0aCBpcyB1c2luZyBgYXJlYSgpYCBpbnNpZGUgYGxheW91dGAsIGFzIHNob3duIGJlbG93LiAKCmBgYHtyIHBhdGNoMTF9CmxheW91dCA8LSBjKAogIGFyZWEodCA9IDIsIGwgPSAxLCBiID0gNSwgciA9IDQpLAogIGFyZWEodCA9IDEsIGwgPSAzLCBiID0gMywgciA9IDUpCiAgKQoKcDMgKyBwNCArIAogIHBsb3RfbGF5b3V0KGRlc2lnbiA9IGxheW91dCkKYGBgCgpXYXRjaCB0aGUgc3BlY2lmaWNhdGlvbiBoZXJlIHdpdGggYHdyYXBfcGxvdHMoKWAKCmBgYHtyIHBhdGNoMTJ9CmxheW91dCA8LSAnCkEjIwojQiMKIyNDCicKd3JhcF9wbG90cyhBID0gcDEsIEIgPSBwMiwgQyA9IHAzLCBkZXNpZ24gPSBsYXlvdXQpCmBgYAoKIyMgRml4ZWQtYXNwZWN0IHBsb3RzIAoKVGhlcmUgYXJlIHNvbWUgcGxvdHMgdGhhdCB1c2UgZml4ZWQgY29vcmRpbmF0ZXMgYW5kIHRoZXNlIHNob3VsZCBub3QgYmUgZGlzdHVyYmVkLiBIZXJlIGlzIGFuIGV4YW1wbGUKCmBgYHtyIHVyYm5tYXByfQpsaWJyYXJ5KHVyYm5tYXByKQoKZ2dwbG90KCkgKwogIGdlb21fcG9seWdvbigKICAgIGRhdGEgPSBzdGF0ZXMsIAogICAgYWVzKHggPSBsb25nLCB5ID0gbGF0LCBncm91cCA9IGdyb3VwLCBmaWxsID0gc3RhdGVfYWJidikKICAgICkgKwogIGNvb3JkX2ZpeGVkKDEuMykgKwogIGdndGhlbWVzOjp0aGVtZV9tYXAoKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogIGxhYnModGl0bGUgPSAiRml4ZWQhISIpIC0+IG15bWFwCgpteW1hcCArIHAxICsgcDIgKyBwMwpgYGAKCiMgTWFwcGluZyAKTWFwcyBhcmUgdmVyeSBwb3dlcmZ1bCB2aXN1YWxpemF0aW9ucyBiZWNhdXNlIHRoZXkgYWxsb3cgeW91IHRvIGhpZ2hsaWdodCBhbmQgcmVmbGVjdCBwYXR0ZXJucywgY2x1c3RlcnMsIHdpdGggcmVsYXRpdmUgZWFzZS4gRm9yIGV4YW1wbGUsIGlzIHBvdmVydHkgcmVhbGx5IGhpZ2hlciBpbiBBcHBhbGFjaGlhbiBjb3VudGllcz8gV2hhdCBhYm91dCB0aGUgcGVyY2VudCBvZiB0aGUgcG9wdWxhdGlvbiB3aXRob3V0IGhlYWx0aCBpbnN1cmFuY2U/IExpdGVyYWN5PyBPcGlvaWQgZGVhdGhzOyBkbyB0aGV5IGZvbGxvdyB0cmFuc3BvcnRhdGlvbiByb3V0ZXM/IFdoYXQgYWJvdXQgQ09WSUQtMTkgY2FzZXM/IE1hcHMgdG8gdGhlIHJlc2N1ZSEKCkJ1aWxkaW5nIGEgbWFwIHJlcXVpcmVzIGEgZmV3IGVsZW1lbnRzLiBGaXJzdCBhbmQgZm9yZW1vc3QsIHlvdSBuZWVkIHNvbWUgZGF0YSB0byBzaG93IG9uIGEgbWFwLiBTZWNvbmQsIHlvdSBuZWVkIHRvIGhhdmUgdGhlIGdlb2dyYXBoaWMgY29vcmRpbmF0ZXMgbmVlZGVkIHRvIGJ1aWxkIGEgbWFwLCBiYXNpY2FsbHkgdGhlIGxhdGl0dWRlIGFuZCBsb25naXR1ZGUgb2YgdGhlIGdlb2dyYXBoaWVzIChzdGF0ZXMsIGNpdGllcywgc2Nob29sIGRpc3RyaWN0cywgZXRjLikgdGhhdCB5b3Ugd2FudCB0byBtYXAuIFRoaXJkLCB5b3Ugd2FudCBhIGNvbHVtbiB0aGF0IGNvbnRhaW5zIHRoZSBuYW1lcyBvZiB0aGUgZ2VvZ3JhcGhpZXMgeW91IHdhbnQgdG8gbWFwLCBhbmQgdGhlc2Ugc2hvdWxkIGJlIHByb3Blcmx5IGZvcm1hdHRlZCAoaS5lLiwgaW4gdGl0bGVjYXNlKSBmb3IgZGlzcGxheWluZyBvbiB0aGUgbWFwLiAKCkxldCB1cyBzdGFydCBieSBidWlsZGluZyBhIHNpbXBsZSBzdGF0ZSBtYXAgd2l0aCB0aGUgYHt1cmJubWFwcn1gIHBhY2thZ2UuIEl0IGNvbWVzIHdpdGggdGhlIG5lY2Vzc2FyeSBkYXRhIGZvciBzdGF0ZXMgYW5kIGNvdW50aWVzLCByZXNwZWN0aXZlbHksIGFuZCB3b3JrcyB3ZWxsIHdpdGggYHtnZ3Bsb3QyfWAuIE5vdGUgdGhlIHJlbGlhbmNlIG9uIGBnZW9tX3BvbHlnb24oKWAgbm93LiAKCmBgYHtyIG1hcDF9CmxpYnJhcnkodXJibm1hcHIpCmdsaW1wc2Uoc3RhdGVzKQpnbGltcHNlKGNvdW50aWVzKQoKZ2dwbG90KCkgKwogIGdlb21fcG9seWdvbihkYXRhID0gc3RhdGVzLCAKICAgICAgICAgICAgICAgYWVzKHggPSBsb25nLCB5ID0gbGF0LCBncm91cCA9IGdyb3VwKSwKICAgICAgICAgICAgICAgZmlsbCA9ICJ3aGl0ZSIsIGNvbG9yID0gInN0ZWVsYmx1ZSIpICsKICBjb29yZF9maXhlZCgxLjMpIApgYGAKCk5vdGUgdGhhdCB0aGlzIGlzIGp1c3QgYW4gZW1wdHkgbWFwIHdpdGggdGhlIHNoYXBlcyBvZiB0aGUgc3RhdGVzLCBhbmQgYWxzbyB0aGF0IEFsYXNrYSBhbmQgSGF3YWlpIGhhdmUgYmVlbiBtb3ZlZCBzbyB0aGF0IHRoZXkgY2FuIGJlIGRpc3BsYXllZCBvbiB0aGUgbWFwLiBXZSBjb3VsZCBidWlsZCBhIG11Y2ggYmV0dGVyIG1hcCBieSByZW1vdmluZyB0aGUgeCBhbmQgeSBheGlzIGxhYmVscyBhbmQgdGljayBtYXJrcywgYW5kIHNldHRpbmcgYSB3aGl0ZSBiYWNrZ3JvdW5kIHVzaW5nIGB0aGVtZV9tYXAoKWAgZnJvbSB0aGUgYHtnZ3RoZW1lc31gIHBhY2thZ2UuIFdlIGNvdWxkIGFsc28gZmlsbCB3aXRoIHNvbWUgY29sb3JzLCBzYXkgb24gdGhlIGJhc2lzIG9mIHRoZSBgc3RhdGVfbmFtZWAuCgpgYGB7ciBtYXAwMn0KZ2dwbG90KCkgKwogIGdlb21fcG9seWdvbihkYXRhID0gc3RhdGVzLCAKICAgICAgICAgICAgICAgYWVzKHggPSBsb25nLCB5ID0gbGF0LCBncm91cCA9IGdyb3VwLCBmaWxsID0gc3RhdGVfbmFtZSksCiAgICAgICAgICAgICAgIGNvbG9yID0gIndoaXRlIikgKwogIGNvb3JkX2ZpeGVkKDEuMykgKwogIGdndGhlbWVzOjp0aGVtZV9tYXAoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKCk9rYXksIHRoaXMgaXMgbm90IHZlcnkgdXNlZnVsIGJlY2F1c2UgaXQgd291bGQgYmUgbXVjaCBiZXR0ZXIgdG8gY29sb3IgdGhlIG1hcCBvbiB0aGUgYmFzaXMgb2Ygc29tZSBzdWJzdGFudGl2ZSB2YXJpYWJsZS4gTGV0IHVzIHNlZSB3aGF0IGx1cmtzIGluIHRoZSBgc3RhdGVkYXRhYCBmaWxlLiAKCmBgYHtyIG1hcDAzfQpnbGltcHNlKHN0YXRlZGF0YSkKYGBgCgpPa2F5LCB0d28gdGhpbmdzIHN0YW5kIG91dCAtLSBgaG9yYXRlYCAodGhlIGhvbWVvd25lcnNoaXAgcmF0ZSksIGFuZCBgbWVkaGhpbmNvbWVgICh0aGUgbWVkaWFuIGhvdXNlaG9sZCBpbmNvbWUpLiBMZXQgdXMgZmlsbCB3aXRoIG1lZGlhbiBob3VzZWhvbGQgaW5jb21lIGJ1dCB0byBkbyBzbywgd2Ugd2lsbCBuZWVkIHRvIGpvaW4gYHN0YXRlZGF0YWAgdG8gb3VyIGBzdGF0ZXNgIGZpbGUuIFdoeT8gQmVjYXVzZSB3ZSBuZWVkIGNvb3JkaW5hdGVzIHRvIG1hcCBhbnl0aGluZyBhbmQgYHN0YXRlZGF0YWAgZG9lcyBub3QgY29udGFpbiBjb29yZGluYXRlcy4gVGhlbiB3ZSBjYW4gc3BlY2lmeSBgZmlsbCA9IG1lZGhoaW5jb21lYCBpbnNpZGUgdGhlIGBhZXMoLi4uKWAgY29tbWFuZC4gCgpgYGB7ciBtYXAwNH0Kc3RhdGVzICU+JQogIGxlZnRfam9pbihzdGF0ZWRhdGEsIGJ5ID0gYygic3RhdGVfZmlwcyIsICJzdGF0ZV9uYW1lIikpIC0+IHN0YXRlLmRmCmdsaW1wc2Uoc3RhdGUuZGYpCmBgYAoKYGBge3IgbWFwMDV9CmdncGxvdCgpICsKICBnZW9tX3BvbHlnb24oZGF0YSA9IHN0YXRlLmRmLCAKICAgICAgICAgICAgICAgYWVzKHggPSBsb25nLCB5ID0gbGF0LCBncm91cCA9IGdyb3VwLCBmaWxsID0gbWVkaGhpbmNvbWUpLAogICAgICAgICAgICAgICBjb2xvciA9ICJ3aGl0ZSIpICsKICBjb29yZF9maXhlZCgxLjMpICsKICBnZ3RoZW1lczo6dGhlbWVfbWFwKCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSArCiAgbGFicygKICAgIHRpdGxlID0gIk1lZGlhbiBIb3VzZWhvbGQgSW5jb21lIGluIHRoZSBTdGF0ZXMgKDIwMTUpIiwKICAgIGZpbGwgPSAiIgogICAgICAgICkgKwogIHNjYWxlX2ZpbGxfdmlyaWRpc19jKG9wdGlvbiA9ICJtYWdtYSIpCmBgYAoKV2hhdCBhYm91dCB3b3JraW5nIHdpdGggY291bnRpZXM/IFN1cmUsIGxldCB1cyBtZXJnZSBgY291bnR5ZGF0YWAgd2l0aCB0aGUgYGNvdW50aWVzIGZpbGUgYW5kIG1hcC4gCgpgYGB7ciBtYXAwNn0KY291bnRpZXMgJT4lCiAgbGVmdF9qb2luKGNvdW50eWRhdGEsIGJ5ID0gYygiY291bnR5X2ZpcHMiKSkgLT4gY291bnR5LmRmCgpnZ3Bsb3QoKSArCiAgZ2VvbV9wb2x5Z29uKGRhdGEgPSBjb3VudHkuZGYsIAogICAgICAgICAgICAgICBhZXMoeCA9IGxvbmcsIHkgPSBsYXQsIGdyb3VwID0gZ3JvdXAsIGZpbGwgPSBtZWRoaGluY29tZSksCiAgICAgICAgICAgICAgIGNvbG9yID0gIndoaXRlIiwgc2l6ZSA9IDAuMDUpICsKICBjb29yZF9maXhlZCgxLjMpICsKICBnZ3RoZW1lczo6dGhlbWVfbWFwKCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSArCiAgbGFicygKICAgIHRpdGxlID0gIk1lZGlhbiBIb3VzZWhvbGQgSW5jb21lIGluIHRoZSBDb3VudGllcyAoMjAxNSkiLAogICAgZmlsbCA9ICIiCiAgICAgICAgKSArCiAgc2NhbGVfZmlsbF92aXJpZGlzX2Mob3B0aW9uID0gIm1hZ21hIikKYGBgCgpNYXliZSB5b3UgYXJlIG9ubHkgaW50ZXJlc3RlZCBpbiBGbG9yaWRhPwoKYGBge3IgbWFwMDd9CmNvdW50eS5kZiAlPiUKICBmaWx0ZXIoc3RhdGVfYWJidiA9PSAiRkwiKSAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9wb2x5Z29uKAogICAgICAgICAgICAgICBhZXMoeCA9IGxvbmcsIHkgPSBsYXQsIGdyb3VwID0gZ3JvdXAsCiAgICAgICAgICAgICAgICAgICBmaWxsID0gbWVkaGhpbmNvbWUpLAogICAgICAgICAgICAgICBjb2xvciA9ICJ3aGl0ZSIsIHNpemUgPSAwLjA1CiAgICAgICAgICAgICAgICkgKwogIGNvb3JkX2ZpeGVkKDEuMykgKwogIGdndGhlbWVzOjp0aGVtZV9tYXAoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpICsKICBsYWJzKAogICAgdGl0bGUgPSAiTWVkaWFuIEhvdXNlaG9sZCBJbmNvbWUgaW4gRmxvcmlhIENvdW50aWVzICgyMDE1KSIsCiAgICBmaWxsID0gIiIKICAgICAgICApICsKICBzY2FsZV9maWxsX3ZpcmlkaXNfYyhvcHRpb24gPSAicGxhc21hIikKYGBgCgpIbW0sIHNvIGZhciBzbyBnb29kIGJ1dCB3aGF0IGlmIHRoZSBkYXRhIHdlcmUgZm9yIHNvbWUgZ2VvZ3JhcGh5IG5vdCBidW5kbGVkIHdpdGggYHt1cmJubWFwcn1gLCBzY2hvb2wgZGlzdHJpY3RzIG9yIHBsYWNlcywgZm9yIGV4YW1wbGU/IE5vdCBhIHByb2JsZW0sIHdlIGp1c3QgaGF2ZSB0byBnbyB0aGUgZXh0cmEgbWlsZS4gRmlyc3Qgd2Ugd291bGQgaGF2ZSB0byBmaW5kLCBkb3dubG9hZCwgYW5kIHVwbG9hZCB0aGUgYHNoYXBlZmlsZWAuIFNheSBJIGFtIGxvb2tpbmcgZm9yIHBsYWNlcyAobG9vc2VseSBkZXNjcmliZWQgYXMgbXVuaWNpcGFsaXRpZXMpIGluIE5ldyBIYW1wc2hpcmUuIFdlbGwsIHRoZSBge3RpZ3Jpc31gIHBhY2thZ2UgY29tZXMgaW4gaGFuZHkgYmVjYXVzZSBpdCBhbGxvd3MgeW91IHRvIGdldCB3aGF0ZXZlciBnZW9ncmFwaHkncyBzaGFwZWZpbGVzIHlvdSB3YW50LiBCZWxvdyBJIGFtIGdldHRpbmcgdGhlIHNoYXBlZmlsZSBmb3IgTmV3IEhhbXBzaGlyZS4gCgpgYGB7ciBwbGFjZXN9CmxpYnJhcnkodGlncmlzKQpwbGFjZXMoCiAgc3RhdGUgPSAiTkgiLCBjYiA9IFRSVUUsIHllYXIgPSAyMDE4LCBwcm9ncmVzc19iYXIgPSBGQUxTRQogICkgLT4gcGxhY2VzLm5oCmBgYAoKT2theSwgc28gbm93IHRoYXQgSSBoYXZlIHRoZSBzaGFwZWZpbGUsIGhvdyBjYW4gSSB1c2UgaXQ/IEkgbmVlZCB0byBgZm9ydGlmeWAgaXQgc28gdGhhdCBpdCBsb29rcyBsaWtlIGEgcmVndWxhciBkYXRhZnJhbWUgcmF0aGVyIHRoYW4gdGhlIG5hdGl2ZSBTcGF0aWFsUG9seWdvbnNEYXRhRnJhbWUgZm9ybWF0IGl0IGNvbWVzIGluLiBXaGVuIEkgZ28gdG8gbWFrZSB0aGUgbWFwIEkgYW0gZ29pbmcgdG8gYWRkIHRoZSBzdGF0ZSBzaGFwZWZpbGUgdG9vIHNpbmNlIG90aGVyd2lzZSB0aGUgc3RhdGUncyBib3VuZGFyeSB3aWxsIG5vdCBzaG93IHVwLiAKCmBgYHtyIG5oMDF9CnBsYWNlcy5uaCAlPiUKICBmb3J0aWZ5KHJlZ2lvbiA9ICJHRU9JRCIpIC0+IG5oLmRmCgpnZ3Bsb3QoKSArCiAgZ2VvbV9wb2x5Z29uKAogICAgZGF0YSA9IHN1YnNldChzdGF0ZS5kZiwgc3RhdGVfbmFtZSA9PSAiTmV3IEhhbXBzaGlyZSIpLAogICAgYWVzKHggPSBsb25nLCB5ID0gbGF0LCBncm91cCA9IGdyb3VwKSwKICAgIGZpbGwgPSAid2hpdGUiLCBjb2xvciA9ICJibGFjayIKICAgICkgKwogIGdlb21fcG9seWdvbigKICAgIGRhdGEgPSBuaC5kZiwKICAgIGFlcyh4ID0gbG9uZywgeSA9IGxhdCwgZ3JvdXAgPSBncm91cCwgZmlsbCA9IGlkLCBjb2xvciA9IGlkKQogICAgKSArCiAgY29vcmRfZml4ZWQoMS4zKSArCiAgZ2d0aGVtZXM6OnRoZW1lX21hcCgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCmBgYAoKT2YgY291cnNlLCB0aGUgYGZpbGxgIGlzIHN1cGVyZmljaWFsIGhlcmUuIEJ1dCBzYXkgd2UgaGFkIHNvbWUgZGF0YSBmb3IgcGxhY2VzIGluIE5ldyBIYW1wc2hpcmUsIG1heWJlIHRoZSBzaXplIG9mIHRoZSBwb3B1bGF0aW9uLCBhcyBpbiBgbmguZGF0YS5SRGF0YWAuIE5vdyB3ZSBjb3VsZCBqb2luIGBuaC5kYXRhYCB3aXRoIGBuaC5kZmAgdG8gY3JlYXRlIGBuaGAgYW5kIHRoZW4gbWFwLiAgCgpgYGB7ciBuaDAyfQpsb2FkKGhlcmU6OmhlcmUoImRhdGEiLCAibmguZGF0YS5SRGF0YSIpKQoKbmguZGYgJT4lCiAgbGVmdF9qb2luKG5oLmRhdGEsIGJ5ID0gYygiaWQiID0gIkdFT0lEIikpIC0+IG5oCgpnbGltcHNlKG5oKQpgYGAKCk5vdyB3ZSBwbG90OgoKYGBge3IgbmgwM30KZ2dwbG90KCkgKwogIGdlb21fcG9seWdvbigKICAgIGRhdGEgPSBzdWJzZXQoc3RhdGUuZGYsIHN0YXRlX25hbWUgPT0gIk5ldyBIYW1wc2hpcmUiKSwKICAgIGFlcyh4ID0gbG9uZywgeSA9IGxhdCwgZ3JvdXAgPSBncm91cCksCiAgICBmaWxsID0gIndoaXRlIiwgY29sb3IgPSAiYmxhY2siCiAgICApICsKICBnZW9tX3BvbHlnb24oCiAgICBkYXRhID0gbmgsCiAgICBhZXMoeCA9IGxvbmcsIHkgPSBsYXQsIGdyb3VwID0gZ3JvdXAsIGZpbGwgPSBwb3B1bGF0aW9uKQogICAgKSArCiAgY29vcmRfZml4ZWQoMS4zKSArCiAgc2NhbGVfZmlsbF92aXJpZGlzX2Mob3B0aW9uID0gInZpcmlkaXMiKSArIAogIGdndGhlbWVzOjp0aGVtZV9tYXAoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpICsKICBsYWJzKAogICAgZmlsbCA9ICIiLAogICAgdGl0bGUgPSAiUG9wdWxhdGlvbiBEaXN0cmlidXRpb24gaW4gTmV3IEhhbXBzaGlyZSdzIFBsYWNlcyIsCiAgICBzdWJ0aXRsZSA9ICIoQW1lcmljYW4gQ29tbXVuaXR5IFN1cnZleSwgMjAxNC0yMDE4KSIKICAgICAgICkKYGBgCgpZb3UgY291bGQgaGF2ZSBhbHNvIGZpbGxlZCBieSBjcmVhdGluZyBxdWFydGlsZXMsIGV0Yy4sIHVzaW5nIGB7U2FudG9rdX1gLCBzbyBkbyBub3QgZm9yZ2V0IHRoYXQgb3B0aW9uLiAKCkJlZm9yZSB3ZSBtb3ZlIG9uLCBvbmUgbW9yZSBtYXAsIHRvIHNob3cgeW91IHRoZSBwb3NzaWJpbGl0aWVzLiBIZXJlLCBJIGFtIApwbG90dGluZyB0aGUgbG9jYXRpb25zIG9mIHRoZSBwYXJraW5nIHRpY2tldHMgaXNzdWVkIGluIFBoaWxhZGVscGhpYSwgdGhpcyB0aW1lIHdpdGggdGhlIGB7bGVhZmxldH1gIHBhY2thZ2UuIEZpcnN0IHRoZSB0aWNrZXRzIGRhdGEtc2V0LCByZWR1Y2VkIHRvIGEgcmFuZG9tIHNhbXBsZSBvZiAyMCUgb2YgdGhlIHRpY2tldHMgaXNzdWVkIGluIHRoZSBtb250aCBvZiBEZWNlbWJlciBpbiAyMDE3ICh0byBrZWVwIHRoZSBkYXRhIHNpemUgbWFuYWdlYWJsZSkuIEkgaGF2ZSBhbHNvIGNyZWF0ZWQgYSBgcG9wdXBgIHRoYXQgd2lsbCBkaXNwbGF5IHNwZWNpZmljIGluZm9ybWF0aW9uIGlmIHNvbWVvbmUgY2xpY2tzIG9uIGEgcG9pbnQgaW4gdGhlIG1hcC4gICAKCmBgYHtyIHBoaWxseX0KcmVhZHI6OnJlYWRfY3N2KCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vcmZvcmRhdGFzY2llbmNlL3RpZHl0dWVzZGF5L21hc3Rlci9kYXRhLzIwMTkvMjAxOS0xMi0wMy90aWNrZXRzLmNzdiIpICU+JQogIG11dGF0ZSgKICAgIHllYXIgPSBsdWJyaWRhdGU6OnllYXIoaXNzdWVfZGF0ZXRpbWUpLAogICAgbW9udGggPSBsdWJyaWRhdGU6Om1vbnRoKGlzc3VlX2RhdGV0aW1lKQogICAgICAgICApICU+JQogIGZpbHRlcihtb250aCA9PSAxMiwgbG9uID4gLTc1LjUpICU+JQogIHNhbXBsZV9mcmFjKDAuMikgLT4gdGlja2V0cwoKdGlja2V0cyAlPiUKICB1bml0ZShkaXNwbGF5LCBjKGlzc3VpbmdfYWdlbmN5LCBpc3N1ZV9kYXRldGltZSwgZmluZSksCiAgICAgICBzZXAgPSAiOyAiLCByZW1vdmUgPSBGQUxTRSkgLT4gdGlja2V0cwoKbGlicmFyeShsZWFmbGV0KQpsaWJyYXJ5KGh0bWx0b29scykKbGlicmFyeSh3aWRnZXRmcmFtZSkKCmxlYWZsZXQodGlja2V0cykgJT4lCiAgYWRkVGlsZXMoKSAlPiUKICBhZGRDaXJjbGVzKGxuZyA9IH4gbG9uLCBsYXQgPSB+IGxhdCwgcG9wdXAgPSB+aHRtbEVzY2FwZShkaXNwbGF5KSwKICAgICAgICAgICAgIGNvbG9yID0gInN0ZWVsYmx1ZSIsIG9wYWNpdHkgPSAwLjEwKSAtPiBsc3QKCmZyYW1lV2lkZ2V0KGxzdCwgd2lkdGggPSAiMTAwJSIsIGhlaWdodCA9ICI0MDAiKQpgYGAKClZvaWxhISBBIGZldyBsaW5lcyBvZiBjb2RlIGFuZCB3ZSBoYXZlIGFuIGludGVyYWN0aXZlIG1hcCB0aGF0IGNhbiBiZSB1c2VkIHRvIGRpc3BsYXkgd2hhdGV2ZXIgZXZpZGVuY2Ugd2Ugd2FudCB0byBkaXNwbGF5LiBOb3RlIHRoYXQgeW91IG5lZWQgZ2VvZ3JhcGhpYyBjb29yZGluYXRlcyBzaW5jZSB3aXRob3V0IHRoZW0gdGhlIGRhdGEgY2Fubm90IGJlIGF0dGFjaGVkIHRvIGEgcGh5c2ljYWwgbG9jYXRpb24uICAKCkxldCB1cyBzZWUgYW5vdGhlciB0d2lzdCBvbiB0aGlzLiBTYXkgSSBhbSB0cnlpbmcgdG8gbWFwIHRoZSB0b3RhbCBudW1iZXIgb2YgQ09WSUQtMTkgY2FzZXMgaW4gT2hpby4gSSBrbm93IHRoZSBjb3VudHkgYW5kIEkga25vdyB0aGUgbnVtYmVyIG9mIGNhc2VzIHRoYXQgb2NjdXJyZWQsIGFzIHdlbGwgYXMgdGhlIGxhdGl0dWRlIGFuZCBsb25naXR1ZGUgb2YgZWFjaCBjb3VudHkuIFdlbGwsIEkgY2FuIGJ1aWxkIGEgc2ltaWxhciBwbG90IGV4Y2VwdCBtYWtpbmcgdGhlIHNpemUgb2YgdGhlIGNpcmNsZSBjb25kaXRpb25hbCB1cG9uIHRoZSBudW1iZXIgb2YgY2FzZXMuIFRoZSBtb3JlIHRoZSBjYXNlcywgdGhlIGxhcmdlciB0aGUgcmFkaXVzIG9mIHRoZSBjaXJjbGUuIAoKYGBge3IgY292MTlhfQpyZWFkcjo6cmVhZF9jc3YoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9ueXRpbWVzL2NvdmlkLTE5LWRhdGEvbWFzdGVyL3VzLWNvdW50aWVzLmNzdiIpIC0+IGNvdmlkIAoKY292aWQgJT4lCiAgZmlsdGVyKHN0YXRlID09ICJPaGlvIiwgZGF0ZSA9PSAiMjAyMC0wNC0xNyIpIC0+IGNvdjE5CmBgYAoKQnV0IHRoZXJlIGFyZSBubyBjb29yZGluYXRlcyBpbiBgY292MTlgLiBXZWxsLCBJIGNhbiBnZXQgdGhvc2UgZnJvbSB0aGUgYHtob3VzaW5nRGF0YX1gIHBhY2thZ2UsIGFuZCB0aGVuIG1lcmdlIHRoYXQgd2l0aCBgY292MTlgLiAKCmBgYHtyIGNvdjE5Yn0KbGlicmFyeShob3VzaW5nRGF0YSkKZ2VvQ291bnR5ICU+JQogIGZpbHRlcihzdGF0ZSA9PSAiT0giKSAlPiUKICBzZXBhcmF0ZShjb3VudHksCiAgICAgICAgICAgaW50byA9IGMoImNvdW50eW5hbWUiLCAiZXh0cmEiKSwKICAgICAgICAgICBzZXAgPSAiIENvdW50eSIsCiAgICAgICAgICAgcmVtb3ZlID0gVFJVRSkgJT4lCiAgbXV0YXRlKGNvdW50eW5hbWUgPSBzdHJpbmdyOjpzdHJfdG9fc2VudGVuY2UoY291bnR5bmFtZSkpIC0+IG9oCgpvaCAlPiUKICBsZWZ0X2pvaW4oY292MTksIGJ5ID0gYygiY291bnR5bmFtZSIgPSAiY291bnR5IikpIC0+IG9oY292MTkKCm9oY292MTkgJT4lCiAgdW5pdGUoZGlzcGxheSwgYyhjb3VudHluYW1lLCBjYXNlcyksIAogICAgICAgIHNlcCA9ICI6ICIsCiAgICAgICAgcmVtb3ZlID0gRkFMU0UpIC0+IG9oY292MTkKCmdsaW1wc2Uob2hjb3YxOSkKYGBgCgpPa2F5LCBub3cgd2UgaGF2ZSBldmVyeXRoaW5nIHdlIG5lZWQgdG8gYnVpbGQgdGhlIG1hcC4gCgpgYGB7ciBvaGNvdjE5Y30KbGVhZmxldChvaGNvdjE5KSAlPiUKICBhZGRUaWxlcygpICU+JQogIGFkZENpcmNsZU1hcmtlcnMoCiAgICBsbmcgPSB+IGxvbiwgbGF0ID0gfiBsYXQsIHBvcHVwID0gfmh0bWxFc2NhcGUoZGlzcGxheSksCiAgICBjb2xvciA9ICJzYWxtb24iLCBvcGFjaXR5ID0gMC4xMCwgcmFkaXVzID0gfnNxcnQoY2FzZXMpCiAgICApIC0+IGxzdAoKZnJhbWVXaWRnZXQobHN0LCB3aWR0aCA9ICIxMDAlIiwgaGVpZ2h0ID0gIjYwMCIpIApgYGAKCgojIEludGVyYWN0aXZlIEdyYXBoaWNzIHdpdGggUGxvdGx5IGFuZCBIaWdoY2hhcnRlcgpJbnRlcmFjdGl2ZSBncmFwaGljcyBhcmUgdXNlZnVsIGluIHNpdHVhdGlvbnMgd2hlcmUgeW91IHdvdWxkIGxpa2UgdGhlIHVzZXIvdmlld2VyIHRvIHNlZSB0aGUgZGF0YSB2YWx1ZXMgb3Igb3RoZXIgZGV0YWlscy4gU2F5LCBmb3IgZXhhbXBsZSwgSSBoYXZlIGEgcGxvdHMgYW5kIHdhbnQgdG8gbWFrZSBpdCBpbnRlcmFjdGl2ZS4gSG93IGNhbiBJIGRvIHRoYXQ/IEJ5IHNhdmluZyBteSByZWd1bGFyIHBsb3QgYW5kIHRoZW4gdXNpbmcgYHtwbG90bHl9YCB0byBhZGQgYSBgZ2dwbG90bHkoKWAgd3JhcHBlciBhcm91bmQgdGhlIHBsb3QuIAoKYGBge3IgcGxvdGx5MDF9CmxpYnJhcnkocGxvdGx5KQoKZ2dwbG90KCkgKwogIGdlb21fcG9pbnQoCiAgICBkYXRhID0gbXBnLAogICAgbWFwcGluZyA9IGFlcyh4ID0gY3R5LCB5ID0gaHd5LCBjb2xvciA9IHRyYW5zKQogICAgKSArCiAgbGFicyh4ID0gIkNpdHkgTWlsZWFnZSIsCiAgICAgICB5ID0gIkhpZ2h3YXkgTWlsZWFnZSIsCiAgICAgICBjb2xvciA9ICJUcmFuc21pc3Npb24iKSAtPiBwbDAxCgpsc3QgPC0gbGlzdCgpCmdncGxvdGx5KHBsMDEpIC0+IGxzdAoKaHRtbHRvb2xzOjp0YWdMaXN0KGxzdCkKYGBgCgpUaGVzZSBwbG90cyBhcmUgdXNlZnVsIHdoZW4gcHJlc2VudGluZyBkYXRhIHRvIGEgbGl2ZSBhdWRpZW5jZSAoaW4gYSB0YWxrLCBvciBvbiB0aGUgd2ViKS4gQnV0LCBJIHByZWZlciBge2hpZ2hjaGFydGVyfWAgc2luY2UgaXQgZG9lcyBhIGxvdCBvZiB0aGluZ3Mgd2VsbCwgYW5kIHRoZXkgYXJlIGFlc3RoZXRpY2FsbHkgcGxlYXNpbmcuIExldCB1cyBzdGF5IHdpdGggdGhlIENPVklELTE5IGV4YW1wbGUuIFNheSBJIHdhbnQgYSBiYXItY2hhcnQgb2YgdGhlIHRvdGFsIG51bWJlciBvZiBjYXNlcyBieSBzdGF0ZS4gCgpgYGB7ciBoaWdoMDF9CmxpYnJhcnkoaGlnaGNoYXJ0ZXIpCgpjb3ZpZCAlPiUKICBmaWx0ZXIoZGF0ZSA9PSAiMjAyMC0wNC0xNyIpICU+JQogIHJlbmFtZShTdGF0ZSA9IHN0YXRlLCBgVG90YWwgQ2FzZXNgID0gY2FzZXMpIC0+IHRhYjEKCmxzdCA8LSBsaXN0KCkKCmhjaGFydCh0YWIxLCAiYmFyIiwgaGNhZXMoeCA9IFN0YXRlLCB5ID0gYFRvdGFsIENhc2VzYCkpIC0+IGxzdCAKCmh0bWx0b29sczo6dGFnTGlzdChsc3QpCmBgYAoKTm90aWNlIHRoZSBrZXkgZWxlbWVudHMgaGVyZTogVGhlIGJhc2ljIGZ1bmN0aW9uIGNhbGwgaXMgYGhjaGFydCgpYCBhbmQgd2UgYXJlIHNwZWNpZnlpbmcgdGhhdCB3ZSB3YW50IGEgYmFyLWNoYXJ0LCBhbmQgd2UgYXJlIGFsc28gcHJvdmlkaW5nIHRoZSBxdWFudGl0aWVzIHRoYXQgc2hvdWxkIGdvIG9uIHRoZSB4IGFuZCB5IGF4aXMsIHJlc3BlY3RpdmVseS4gTm90ZSB0aGF0IHggYWN0dWFsbHkgZW5kcyB1cCBhcyB0aGUgeSB3aGVuIHlvdSBzcGVjaWZ5IGEgImJhciIgY2hhcnQuIAoKV2hhdCBpZiBJIHdhbnRlZCBhIGxpbmUtY2hhcnQsIG1heWJlIG9mIHRoZSBudW1iZXIgb2YgY2FzZXMgb3ZlciB0aW1lPyBBbmQgSSB3YW50ZWQgdGhpcyBqdXN0IGZvciBhIGZldyBzdGF0ZXM/IFdlIGNvdWxkIGRvIHRoYXQgdG9vLCBhcyBzaG93biBiZWxvdy4gTm90ZSB0aGF0IEkgYW0gY3JlYXRpbmcgYHRhYjJgLCBhIGZyZXF1ZW5jeSB0YWJsZSBvZiB0aGUgbnVtYmVyIG9mIGNhc2VzIGJ5IHN0YXRlIGFuZCBkYXRlLCBhbmQgdGhlbiBjb252ZXJ0aW5nIHRvdGFsX2Nhc2VzIGludG8gYSBsb2dhcml0aG1pYyBmb3JtIChzYXZlZCBhcyBgbG9nX2Nhc2VzYCkgc28gdGhhdCB3ZSBjYW4gY29tcGFyZSB0aGUgcmF0ZSBvZiBjaGFuZ2UgZnJvbSBvbmUgZGF0ZSB0byB0aGUgbmV4dCBvbiBhIGNvbW1vbiBzY2FsZS4gIAoKYGBge3IgaGlnaDAyfQpjb3ZpZCAlPiUKICBmaWx0ZXIoc3RhdGUgJWluJSBjKCJPaGlvIiwgIkZsb3JpZGEiLCAiQ2FsaWZvcm5pYSIsICJOZXcgSmVyc2V5IiwgIk9oaW8iLCAiTmV3IFlvcmsiKSwKICAgICAgICAgZGF0ZSA+PSAiMjAyMC0wMy0xNSIpICU+JQogIGdyb3VwX2J5KHN0YXRlLCBkYXRlKSAlPiUKICBtdXRhdGUobG9nX2Nhc2VzID0gbG9nKGNhc2VzKSkgLT4gdGFiMgoKbHN0IDwtIGxpc3QoKQoKaGNoYXJ0KHRhYjIsICJsaW5lIiwgaGNhZXMoeCA9IGRhdGUsIHkgPSBsb2dfY2FzZXMsIGdyb3VwID0gc3RhdGUpKSAtPiBsc3QKCmh0bWx0b29sczo6dGFnTGlzdChsc3QpCmBgYAoKTm93IGhlcmUgaXMgYSBjb3VudHktbGV2ZWwgY2hhcnQgdGhhdCBzaG93cyB0aGUgdG90YWwgbnVtYmVyIG9mIGNhc2VzIGFzIG9mIEFwcmlsIDE0LCAyMDIwLiBUaGlzIGlzIHN0b3JlZCBpbiBgdGFiM2AgY3JlYXRlZCBhcyBzaG93biBiZWxvdy4gUGF5IGF0dGVudGlvbiB0byB0aGlzIGNyZWF0aW9uIGJlY2F1c2Ugd2UgYXJlIG5vdCBqdXN0IGNyZWF0aW5nIGEgZnJlcXVlbmN5IHRhYmxlIGJ1dCBhbHNvIGFkZGluZyBpbiBhIHNwZWNpZmljIGtleSB3ZSBhcmUgY2FsbGluZyBgY29kZWAgYmVjYXVzZSB3ZSB3aWxsIG5lZWQgdG8gam9pbiB0aGVzZSBkYXRhIHRvIHRoZSBtYXAgZGF0YS4KCmBgYHtyIGhpZ2gwM30KY292aWQgJT4lCiAgZ3JvdXBfYnkoY291bnR5LCBzdGF0ZSwgZmlwcykgJT4lCiAgZmlsdGVyKGRhdGUgPT0gIjIwMjAtMDQtMTciKSAlPiUgCiAgdW5pdGUoTG9jYXRpb24sIGMoY291bnR5LCBzdGF0ZSksIHNlcCA9ICIsICIsIHJlbW92ZSA9IFRSVUUpIC0+IHRhYjMKCmxpYnJhcnkodXJibm1hcHIpCmRhdGEoY291bnRpZXMpCmNvdW50aWVzICU+JQogIHNlcGFyYXRlKGNvdW50eV9maXBzLCBpbnRvID0gYygic3RmaXBzIiwgImZpcHMiKSwgCiAgICAgICAgICAgc2VwID0gMiwgcmVtb3ZlID0gRkFMU0UpICU+JQogIG11dGF0ZShsZWFkZXIgPSAidXMiLCAKICAgICAgICAgc3Rsb3dlciA9IHN0cmluZ3I6OnN0cl90b19sb3dlcihzdGF0ZV9hYmJ2KSkgJT4lCiAgdW5pdGUoY29kZSwgYyhsZWFkZXIsIHN0bG93ZXIsIGZpcHMpLCBzZXAgPSAiLSIpIC0+IGNkZgoKY2RmICU+JQogIHNlbGVjdChjb2RlLCBjb3VudHlfZmlwcykgJT4lCiAgZGlzdGluY3QoKSAtPiBjZGYyCgp0YWIzICU+JQogIGxlZnRfam9pbihjZGYyLCBieSA9IGMoImZpcHMiID0gImNvdW50eV9maXBzIikpIC0+IHRhYjQKYGBgCgpIZXJlIGNvbWVzIHRoZSBtYXAhIAoKYGBge3IgaGlnaDA0fQpsaWJyYXJ5KHZpcmlkaXMpCgpsc3QgPC0gbGlzdCgpCgpoY21hcCgiY291bnRyaWVzL3VzL3VzLWFsbC1hbGwiLCAKICAgICAgZGF0YSA9IHRhYjQsCiAgICAgIG5hbWUgPSAiQ09WSUQtMTkgQ2FzZXMiLCB2YWx1ZSA9ICJjYXNlcyIsCiAgICAgIGpvaW5CeSA9IGMoImhjLWtleSIsICJjb2RlIiksCiAgICAgIGJvcmRlckNvbG9yID0gInN0ZWVsYmx1ZSIpICU+JQogIGhjX2NvbG9yQXhpcyhzdG9wcyA9IGNvbG9yX3N0b3BzKDEwLCByZXYobWFnbWEoMTApKSkpICU+JSAKICBoY19sZWdlbmQobGF5b3V0ID0gImhvcml6b250YWwiLCBhbGlnbiA9ICJyaWdodCIsCiAgICAgICAgICAgIGZsb2F0aW5nID0gVFJVRSwgdmFsdWVEZWNpbWFscyA9IDAsIHZhbHVlU3VmZml4ID0gIiIpIC0+IGxzdAoKaHRtbHRvb2xzOjp0YWdMaXN0KGxzdCkKYGBgCgpOb3RlIHRoYXQgYGNvdW50cmllcy91cy91cy1hbGwtYWxsYCBpbmRpY2F0ZXMgdGhhdCB3ZSB3YW50IGNvdW50aWVzLiBJZiB3ZSB3YW50ZWQgdGhlIHN0YXRlcyBpbnN0ZWFkIGl0IHdvdWxkIGhhdmUgYmVlbiBgY291bnRyaWVzL3VzL3VzLWFsbGAuIFRoZSBgdmFsdWUgPSBgIGZsYWdzIHRoZSBjb2x1bW4gdGhhdCBoYXMgdG8gYmUgdXNlZCB0byBwb3B1bGF0ZSB0aGUgY29sb3Igc2NoZW1lLiBUaGUgYGpvaW5CeWAgY29ubmVjdHMgb3VyIGRhdGEgKGB0YWI0YCkgdG8gYGhjbWFwKClgIHNvIHRoYXQgYHRvdGFsX2Nhc2VzYCBnZXRzIGF0dGFjaGVkIHRvIHRoZSBjb3JyZWN0IGxvY2F0aW9uIGluIHRoZSBtYXAuIAoKCldoYXQgaWYgd2Ugd2FudGVkIG9ubHkgT2hpbz8gV2VsbCwgaW4gdGhhdCBjYXNlIHdlIGNvdWxkIHN1YnNldCBhcyBzaG93biBiZWxvdzoKCmBgYHtyIGhpZ2gwNX0KdGFiNCAlPiUKICBmaWx0ZXIoZ3JlcGwoIi1vaC0iLCBjb2RlKSkgLT4gdGFiNQoKbHN0IDwtIGxpc3QoKQoKaGNtYXAoImNvdW50cmllcy91cy91cy1vaC1hbGwiLCAKICAgICAgZGF0YSA9IHRhYjUsCiAgICAgIG5hbWUgPSAiQ09WSUQtMTkgQ2FzZXMiLCB2YWx1ZSA9ICJjYXNlcyIsCiAgICAgIGpvaW5CeSA9IGMoImhjLWtleSIsICJjb2RlIiksCiAgICAgIGJvcmRlckNvbG9yID0gInN0ZWVsYmx1ZSIpICU+JQogIGhjX2NvbG9yQXhpcyhzdG9wcyA9IGNvbG9yX3N0b3BzKDEwLCByZXYobWFnbWEoMTApKSkpICU+JSAKICBoY19sZWdlbmQobGF5b3V0ID0gImhvcml6b250YWwiLCBhbGlnbiA9ICJyaWdodCIsCiAgICAgICAgICAgIGZsb2F0aW5nID0gVFJVRSwgdmFsdWVEZWNpbWFscyA9IDAsIHZhbHVlU3VmZml4ID0gIiIpIC0+IGxzdAoKaHRtbHRvb2xzOjp0YWdMaXN0KGxzdCkKYGBgCgojIFByYWN0aWNlIEV4ZXJjaXNlcwojIyBFeGVyY2lzZSAoMSkgey19CkNyZWF0ZSBhIG1hcCBvZiBhbGwgdGhlIGNvdW50aWVzIGluIE5ldyBZb3JrLiBCZSBzdXJlIHRvIHRpdGxlIHRoZSBtYXAgYW5kIHRvIGZpbGwgaW4gZWFjaCBjb3VudHkgd2l0aCB0aGUgdG90YWwgbnVtYmVyIG9mIENPVklEMTkgY2FzZXMgdGhleSBoYXZlIHNlZW4gdG8gZGF0ZS4gSW4gYWRkaXRpb24sIGRyYXcgY291bnR5IGJvcmRlcnMgaW4gd2hpdGUuIFVzZSBgdGhlbWVfbWFwKClgIGFuZCBtYWtlIHN1cmUgdGhlIGxlZ2VuZCBpcyBhdCB0aGUgYm90dG9tLiBbKipIaW50OioqIFlvdSB3aWxsIG5lZWQgdG8gY2FsY3VsYXRlIHRoZSB0b3RhbCBudW1iZXIgb2YgY2FzZXMgcGVyIGNvdW50eSBhbmQgdGhlbiBqb2luIHRoZSByZXN1bHRpbmcgZmlsZSB3aXRoIHRoZSBjb3VudGllcyBkYXRhIGZpbGUgdG8gZ2V0IHRoZSBsYXRpdHVkZS9sb25naXR1ZGVzIGZvciB0aGUgY291bnRpZXMuXQoKIyMgRXhlcmNpc2UgKDIpIHstfQpSdW4gdGhlIGZvbGxvd2luZyBjb2RlIGNodW5rIHRvIGxvYWQgZGF0YSBvbiB0aGUgbXVyZGVyLCBhc3NhdWx0IGFuZCByYXBlIHJhdGVzIHBlciAxMDAsMDAwIHBlcnNvbnMuIGBVcmJhbnBvcGAgaXMgdGhlIHBlcmNlbnQgb2YgdGhlIHN0YXRlIHBvcHVsYXRpb24gdGhhdCBsaXZlcyBpbiBhbiB1cmJhbiBhcmVhLiAKCmBgYHtyIHVzYS1kYXRhfQpkYXRhKFVTQXJyZXN0cykKbmFtZXMoVVNBcnJlc3RzKQpVU0FycmVzdHMkc3RhdGVuYW1lIDwtICByb3duYW1lcyhVU0FycmVzdHMpCmBgYAoKTm93IGNyZWF0ZSBhIHN0YXRlLWxldmVsIG1hcCBvZiB0aGUgNTAgc3RhdGVzIG1ha2luZyBzdXJlIHRvIHVzZSBgVXJiYW5Qb3BgIHRvIGZpbGwgZWFjaCBzdGF0ZS4gVGl0bGUgdGhlIG1hcCBhbmQgcGxhY2UgdGhlIGxlZ2VuZCBhdCB0aGUgYm90dG9tLiAKCgojIyBFeGVyY2lzZSAoMykgey19ClVzZSB0aGUgYFVTQXJyZXN0c2AgZGF0YSB0byBkcmF3IHNjYXR0ZXJwbG90cyBvZiAoYSkgYE11cmRlcmAgdmVyc3VzIGBVcmJhblBvcGAsIChiKSBgQXNzYXVsdGAgdmVyc3VzIGBVcmJhblBvcGAsIGFuZCAoYykgYFJhcGUgIHZlcnN1cyBVcmJhblBvcGAuIFNhdmUgZWFjaCBvZiB0aGVzZSBzY2F0dGVycGxvdHMgYnkgbmFtZSBhbmQgdGhlbiB1c2UgYHBhdGNod29ya2AgdG8gY3JlYXRlIGEgc2luZ2xlIGNhbnZhcyB0aGF0IGluY2x1ZGVzIGFsbCB0aHJlZSBwbG90cy4gTWFrZSBzdXJlIHlvdSBsYWJlbCB0aGUgeC1heGlzLCB5LWF4aXMsIGFuZCB0aXRsZSBlYWNoIHBsb3QuIAoKIyMgRXhlcmNpc2UgKDQpIHstfQpOb3cgY3JlYXRlIGBoaWdoY2hhcnRlcmAgdmVyc2lvbnMgb2YgZWFjaCBvZiB0aGUgdGhyZWUgc2NhdHRlcnBsb3RzIHlvdSBjcmVhdGVkIGluIEV4ZXJjaXNlICgzKSBhYm92ZS4gWW91IHNob3VsZCBlbmQgdXAgd2l0aCB0aHJlZSBzY2F0dGVycGxvdHMsIGVhY2ggb24gaXRzIG93biBjYW52YXMuIAoKIyMgRXhlcmNpc2UgKDUpIHstfQpUaGUgYHNmdHJlZXNgIGRhdGFzZXQgaW5jbHVkZXMgdmFyaW91cyBwaWVjZXMgb2YgZGF0YSBvbiB0cmVlcyBpbiBTYW4gRnJhbmNpc2NvLiBJbiB0dXJuLCBgc2YuZGZgIGlzIGEgMTAlIHJhbmRvbSBzYW1wbGUgb2YgdGhlc2UgdHJlZXMuIAoKVXNlIGBsZWFmbGV0YCB0byBjcmVhdGUgYSBtYXAgdGhhdCBzaG93cywgYXMgY2lyY2xlcywgdGhlIGxvY2F0aW9uIG9mIGVhY2ggdHJlZSwgYW5kIG1ha2Ugc3VyZSB3aGVuIHNvbWVvbmUgY2xpY2tzIG9uIHRoZSBjaXJjbGUgdGhlIHBvcHVwIHNob3dzIHRoZSBgc3BlY2llc2AuIEFsc28gZW5zdXJlIHRoYXQgdGhlIGNpcmNsZXMgYXJlIGdyZWVuIGluIGNvbG9yLiAKCmBgYHtyIHNmdHJlZXN9CnJlYWRyOjpyZWFkX2NzdignaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3Jmb3JkYXRhc2NpZW5jZS90aWR5dHVlc2RheS9tYXN0ZXIvZGF0YS8yMDIwLzIwMjAtMDEtMjgvc2ZfdHJlZXMuY3N2JykgLT4gc2ZfdHJlZXMKCnNmX3RyZWVzICU+JQogIHNhbXBsZV9mcmFjKDAuMTApIC0+IHNmLmRmCmBgYAoK