Data Science Module

Topic 2B: Data Visualisation II


In this computer lab, we will extend our appreciation of the potential of the plotly (Sievert 2020) R package, and create some informative, interactive data visualisations, using data from the penguins data set. The types of plots we consider in this lab were introduced in Topic 2.

By the end of this lab, you should feel comfortable creating a customized, interactive scatter plot, and be able to combine different plotly graphs together in a single, customized display.


1 Palmer Penguins Data Set

To begin, let’s quickly refresh our memories of the penguins data set from the palmerpenguins R package (Horst, Hill, and Gorman 2020). This data set contains information on 3 species of penguin, who live on different islands in the Palmer archipelago, off the coast of Antarctica. For more details, you can refer to Section 2 of the Data Visualisation in R supplement.

Note: If you do not have the palmerpenguins package downloaded, just click on the Code box below, and run the code that appears:

install.packages("palmerpenguins")

Open up RStudio, and run the following code to load and summarise the palmerpenguins package.

Note that the package is called palmerpenguins, but once this is loaded, the actual data to access in R is stored in the object penguins.

# This code loads the `palmerpenguins` package into your current R working environment.
library(palmerpenguins)
# This code summarises the data in the `palmerpenguins` package.
summary(penguins)

Don’t worry too much about the values shown in the summary table - the main things to note at this stage are the different variables, namely species, island, bill_length_mm, bill_depth_mm, flipper_length_mm, body_mass_g, sex and year.

2 Plotly Scatter Plots

From our summary table, we can see that the measurement variables for the penguins include body_mass_g and flipper_length_mm. It seems reasonable to think that penguins with larger body masses might also have longer flippers.

To visualise this, and check our assumption, we could use a scatter plot. To create this scatter plot, we will use the plotly package, which offers several benefits over using the default plotting options in R. For more details on plotly, you can refer to Section 3 of the Data Visualisation in R supplement.

2.1

First, let’s load the plotly package. Using our process in 1 as a guide, load the plotly package in R. (If you do not have the plotly package downloaded, make sure to do this now too).

Hint: Check the code chunk below if you are not sure how to proceed.

install.packages("plotly")
library(plotly)

2.2

We can create a plotly plot using the function plot_ly(). Let’s take a look at the typical composition of a plotly plot:

plot_name <- plot_ly(data = ..., x = ~ ..., y = ~ ...)

Let’s break this down.

  • Firstly, (using the assignment operator <-) we assign a name to our plot - here we have chosen the generic plot_name.
  • Next, within plotly(), we specify the main arguments of the function.
  • The data = ... part tells R what data we are analysing.
  • The x = ~ ... part tells R which variable in our data set to plot on the x-axis of our plot.
  • The y = ~ ... part tells R which variable in our data set to plot on the y-axis of our plot.

Note that we simply replace the ...s with whatever data we are using.

The code below will create a simple scatter plot of flipper_length_mm versus body_mass_g. Make sure to inspect this code, and check that you understand each component. Since we have specified our data set is penguins, we don’t then need to do this when specifying our x and y inputs - we can simply specify any of the variables contained within this data set.

penguins_scatter <- plot_ly(data = penguins, x = ~body_mass_g, y = ~flipper_length_mm)
penguins_scatter

Note that once we assign the plot to the object penguins_scatter, we then have to run this object in a subsequent line, in order for the plot to be rendered.

2.3

One key benefit of plotly graphs compared to base R graphs, is that the plotly graphs are interactive!

Notice is that if you hover over the data in the scatter plot, you can see the specific coordinates of each point. If you left-click and drag your cursor over a section to create a box, you can also zoom in on a particular section of the plot. Just double left-click to zoom back out.

2.4

As we suspected, it seems quite clear that as the body mass of penguins increases, so too does their flipper length.

But our graph is quite basic at the moment - we can do better.

Another great aspect of plotly graphs is that it is very easy to include a third variable which can help to further distinguish the data plotted on the x and y axes. We can do so by adding the argument color = ~... within plotly().

Perhaps the body_mass_g and flipper_length_mm of the penguins is also related to their sex? Let’s take a look at how our scatter plot changes, if we distinguish between male and female penguins.

penguins_scatter2 <- plot_ly(data = penguins, x = ~body_mass_g, y = ~flipper_length_mm, 
                             color = ~sex)
penguins_scatter2

That’s looking a bit nicer! Now we can see that most of the smaller penguins are female, and most of the larger penguins are male. If you hover over the data, you’ll notice that the sex is now shown alongside the coordinates of each data point.

We also have a helpful legend in the top right. This is not only useful as a guide - try clicking on one of the labels in the legend.

2.5

While our scatter plot is looking better, the default colours chosen to distinguish between male and female penguins are quite similar. Perhaps we would like more contrast?

To specify the set of colours to use for the plot, we can add the additional argument colors = ... to our plot_ly function. This argument accepts any valid R colour codes. Take a look at this pdf for an overview of different colours we can use in R.

Complete and then run the code below to change the colours you use in your scatter plot.

penguins_scatter_colours <- plot_ly(data = penguins, 
                                    x = ~body_mass_g, y = ~flipper_length_mm, 
                                    color = ~sex, colors = ...)
penguins_scatter_colours

Hint: You will need a combination of two colours. Check the Code box below if you are stuck.

# If you are specifying specific individual colours, you will need to use the layout 
colors = c("...", "...").

2.6

If you do not want to spend too much time customising the colours used in your plots, there are pre-existing sets of colours you can use. Try setting your colors =... argument in 2.5 sequentially to colors = "Set1", then to colors = "Set2" and finally to colors = "Set3". Do any particular sets appeal to you?

2.7

There are many different display options for plot_ly graphics, and if you try running the R commands

penguins_scatter2 <- plot_ly(data = penguins, x = ~body_mass_g, y = ~flipper_length_mm, 
                             color = ~sex, colors = "Set1")
penguins_scatter2

you may see some red Warning messages appear in the R Console. Often, you don’t have to worry about these, but if you would like to minimise them, you can add the following arguments to your plot_ly function.

penguins_scatter2 <- plot_ly(data = penguins, x = ~body_mass_g, y = ~flipper_length_mm, 
                             color = ~sex, colors = "Set1",
                             type = "scatter", mode = "markers")
penguins_scatter2

Here, we have included the additional arguments type = ... and mode = ....

  • We set type = "scatter" to ensure our data is plotted as a scatter plot.
  • We set mode = "markers" to ensure that each of our data points is plotted individually.

These additional arguments are often helpful, as sometimes we like to have a little more control over how our data is presented.

You’ll notice however that if these commands are omitted from your function, R will just work out what it thinks is the optimal presentation format (hence the warning messages informing us which options R has selected, since some details haven’t been user-specified).

This is often for the best - try changing the mode = "markers" section of code to mode ="lines" and then re-running the plot. What happens?

2.8

So far, we have treated all the penguins as one large group, differentiated by sex. However, we actually have data for three separate species of penguin - Adelie, Chinstrap, and Gentoo.

We have already used different colours to differentiate the male and female penguins, but so far all the data points are the same symbol - a dot. We can use the additional argument symbol = ... within our plot_ly function to further improve our graph, and distinguish between the different species of penguin.

Take a look at the R code and resultant graph below:

penguins_scatter3 <- plot_ly(data = penguins, x = ~body_mass_g, y = ~flipper_length_mm, 
                             color = ~sex, colors = "Set1", symbol = ~species, 
                             type = "scatter", mode = "markers")
penguins_scatter3

Great! This is looking much more informative than our initial scatter plot. Now it’s quite clear for instance that the majority of larger penguins (both male and female) are of the Gentoo species, which we couldn’t discern from our previous version of the scatter plot.

2.9

Just as R has many colour options available, so too are there many symbol options available. Since we have not specified which specific symbols to use in 2.8, R has used the first default 3.

We used colors = ... to modify our color = ... specification, and similarly, we can use symbols = ... to modify our symbol = ... specification.

There are 26 different base R symbols you can choose from - these can be specified either by number, or by name. Some of the names are quite long, e.g. "filled triangle point-up", so it is often easier to use numbers. However, some names are easy to remember - take a look at the table below.

Table 2.1: Symbol Options
Number Name
0 square
1 circle
2 triangle point up
3 plus
4 cross
5 diamond
8 star

using 2.1 and the symbols = ... argument, change the symbols used in the penguins_scatter3 scatter plot created in 2.8.

Hint: If you are using symbol names, and your code isn’t working, check the code chunk below.

# Note that just like for the colours argument, if you are using words, 
# these need to be surrounded with quotation marks,
# e.g. "square", or 'square' will work, but square will not

2.10

As a final touch, you may also like to change the size of the symbols in your scatter plot. To do so, we can include the marker = ... argument in our plot_ly function.

This is a little more complicated to use than our previous arguments, as multiple specifications can be made within this argument. As a result, we use the format marker = list(...). Within the list() function, we can include multiple specifications which all pertain to the marker argument.

To change the size of the symbols, we use the appropriately named size = argument, within the list() function. As a result, if we want to change the default marker size (6) to be a little larger, we could include the argument marker = list(size = 8) within our plot_ly function for our penguins_scatter3 scatter plot created in 2.8.

Try this now, observe the changes, and then try increasing and decreasing the marker size.

3 Creating your own Plotly Scatter Plot

Now that we have covered the basics of creating plotly scatter plots in 2, it’s time for you to create your own using the penguins data set.

3.1

To begin, try creating a simple plotly scatter plot of bill_length_mm versus body_mass_g.

If you’re not sure that you’re on the right track, refer back to 2.2, and/or check the code below:

penguins_scatter_new <- plot_ly(data = penguins, x = ~body_mass_g, y = ~...
                             type = "scatter", mode = "markers")
penguins_scatter_new

3.2

Once you are happy with your initial scatter plot, try using the color = ~ argument to differentiate the data in your plot by island. Do you notice any patterns?

You can refer back to 2.4 if you are not sure how to proceed.

3.3

Next, use the symbol = ~ argument to show different symbols for each species in your plot.

3.4

To finish off your plot, change the symbols in your plot, and increase the marker size of your symbols slightly.

3.5

Does it seem like penguins living on different islands have noticeably different body_mass_g or bill_length_mm measurements?

4 Mixed Subplots

Recall from our first Data Science Computer Lab how we created some histograms for our palmerpenguins data set. Some of the code used for that lab is reproduced below:

penguin_hist <- plot_ly(data = penguins, x = ~body_mass_g, color = ~island, type = "histogram", alpha = 0.6)

penguin_hist <- penguin_hist %>% layout(yaxis = list(title = 'count'), barmode ="overlay")
penguin_hist

Suppose that we would like to present all our palmerpenguins data visualisations together. We can do this using the subplot function.

4.1

Take a look at the R code below:

penguin_combined_plots <- subplot(penguins_scatter3, penguin_hist, 
                                  nrows = 2, margin = 0.05) 
penguin_combined_plots <- penguin_combined_plots %>% 
                          layout(title = "Palmer Penguin Data",
                                 xaxis = list(title = 'body_mass_g'), 
                                 yaxis = list(title = "flipper_length_mm"),
                                 xaxis2 = list(title = 'body_mass_g'), 
                                 yaxis2 = list(title = "count"))

Note that here:

  • We are using the subplot command to plot the penguins_scatter3 and penguin_hist plots together.
  • The nrows = 2 argument tells R to produce these plots in 2 rows.
  • The margin = 0.05 argument tells R to leave a small margin between the two plots.
  • The subsequent lines of code are used to add a title to our selection of plots, and add axes labels to the plots - note that we use xaxis to define the x-axis label for the first plot, and xaxis2 to define the x-axis label for the second plot (and similarly for the y-axes).

When we now run this object penguin_combined_plots, we obtain the following:

penguin_combined_plots

Note that the two plots are still wholely interactive. The legends have been combined, and can be used to filter the individual plots.

While we have only combined two plots here, the subplot function can be used to present several plots together, which can be particularly informative when you would like to display multiple aspects of your data simultaneously. The only major downside of presenting plots together using subplot is that their axes labels are removed by default, and must be respecified, as above.

4.2

Using the information from 4.1, try to combine the scatter plot you produced in 3 with the histogram shown above at the start of 4.

Hint: You don’t need to write any code for the histogram, you can simply use the R code shown at the start of 4.


Great job, that’s everything for today!

Hopefully you now feel confident creating plotly scatter plots. Don’t worry if some of the code seems difficult at the moment - we are only at the second lab, and we will have plenty of time to practice and improve as the semester progresses.

Before you finish up, make sure to save your script file somewhere safe - it might come in handy later on.


References

Horst, Allison Marie, Alison Presmanes Hill, and Kristen B Gorman. 2020. Palmerpenguins: Palmer Archipelago (Antarctica) Penguin Data. https://doi.org/10.5281/zenodo.3960218.
Sievert, Carson. 2020. Interactive Web-Based Data Visualization with r, Plotly, and Shiny. Chapman; Hall/CRC. https://plotly-r.com.


These notes have been prepared by Rupert Kuveke. The copyright for the material in these notes resides with the author named above, with the Department of Mathematical and Physical Sciences and with La Trobe University. Copyright in this work is vested in La Trobe University including all La Trobe University branding and naming. Unless otherwise stated, material within this work is licensed under a Creative Commons Attribution-Non Commercial-Non Derivatives License BY-NC-ND.

LS0tDQp0aXRsZTogIlNUTTEwMDE6IENvbXB1dGVyIExhYiAyQiINCm91dHB1dDoNCiAgYm9va2Rvd246Omh0bWxfZG9jdW1lbnQyOiANCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZmxvYXQ6IHRydWUNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgdGhlbWU6IHJlYWRhYmxlDQogICAgY29kZV9mb2xkaW5nOiBzaG93DQpiaWJsaW9ncmFwaHk6IFNUTTEwMDFfRFNfQ0xfcmVmZXJlbmNlcy5iaWIgDQpsaW5rLWNpdGF0aW9uczogeWVzDQotLS0NCg0KPHN0eWxlPg0KI1RPQyB7DQogIGJhY2tncm91bmQ6IHVybCgiaHR0cHM6Ly93d3cubGF0cm9iZS5lZHUuYXUvX21lZGlhL2xhLXRyb2JlLWFwaS92NS9pbWcvbG9nby5zdmciKTsNCiAgYmFja2dyb3VuZC1zaXplOiBjb250YWluOw0KICBwYWRkaW5nLXRvcDogODBweCAhaW1wb3J0YW50Ow0KICBiYWNrZ3JvdW5kLXJlcGVhdDogbm8tcmVwZWF0Ow0KfQ0KPC9zdHlsZT4NCg0KIyMjIERhdGEgU2NpZW5jZSBNb2R1bGUgey19DQoNCiMjIyBUb3BpYyAyQjogRGF0YSBWaXN1YWxpc2F0aW9uIElJIHstfQ0KDQo8YnI+DQoNCkluIHRoaXMgY29tcHV0ZXIgbGFiLCB3ZSB3aWxsIGV4dGVuZCBvdXIgYXBwcmVjaWF0aW9uIG9mIHRoZSBwb3RlbnRpYWwgb2YgdGhlIGBwbG90bHlgIFtAcGxvdGx5XSBSIHBhY2thZ2UsIGFuZCBjcmVhdGUgc29tZSBpbmZvcm1hdGl2ZSwgaW50ZXJhY3RpdmUgZGF0YSB2aXN1YWxpc2F0aW9ucywgdXNpbmcgZGF0YSBmcm9tIHRoZSBgcGVuZ3VpbnNgIGRhdGEgc2V0LiBUaGUgdHlwZXMgb2YgcGxvdHMgd2UgY29uc2lkZXIgaW4gdGhpcyBsYWIgd2VyZSBpbnRyb2R1Y2VkIGluIFtUb3BpYyAyXShodHRwczovL2Jvb2tkb3duLm9yZy9hX3NoYWtlci9TVE0xMDAxX1RvcGljXzIvKS4NCg0KQnkgdGhlIGVuZCBvZiB0aGlzIGxhYiwgeW91IHNob3VsZCBmZWVsIGNvbWZvcnRhYmxlIGNyZWF0aW5nIGEgY3VzdG9taXplZCwgaW50ZXJhY3RpdmUgc2NhdHRlciBwbG90LCBhbmQgYmUgYWJsZSB0byBjb21iaW5lIGRpZmZlcmVudCBwbG90bHkgZ3JhcGhzIHRvZ2V0aGVyIGluIGEgc2luZ2xlLCBjdXN0b21pemVkIGRpc3BsYXkuDQoNCjxicj4NCg0KIyBQYWxtZXIgUGVuZ3VpbnMgRGF0YSBTZXQgeyNwZW5ndWluc30NCg0KVG8gYmVnaW4sIGxldCdzIHF1aWNrbHkgcmVmcmVzaCBvdXIgbWVtb3JpZXMgb2YgdGhlIGBwZW5ndWluc2AgZGF0YSBzZXQgZnJvbSB0aGUgYHBhbG1lcnBlbmd1aW5zYCBSIHBhY2thZ2UgW0BwZW5ndWluc10uIA0KVGhpcyBkYXRhIHNldCBjb250YWlucyBpbmZvcm1hdGlvbiBvbiAzIHNwZWNpZXMgb2YgcGVuZ3Vpbiwgd2hvIGxpdmUgb24gZGlmZmVyZW50IGlzbGFuZHMgaW4gdGhlIFBhbG1lciBhcmNoaXBlbGFnbywgb2ZmIHRoZSBjb2FzdCBvZiBBbnRhcmN0aWNhLiBGb3IgbW9yZSBkZXRhaWxzLCB5b3UgY2FuIHJlZmVyIHRvIFtTZWN0aW9uIDIgb2YgdGhlIERhdGEgVmlzdWFsaXNhdGlvbiBpbiBSIHN1cHBsZW1lbnRdKGh0dHBzOi8vYm9va2Rvd24ub3JnL3JlaGsvc3RtMTAwMV9kc21fdDFfZGF0YV92aXN1YWxpc2F0aW9uX2luX3IvcGVuZ3VpbnMuaHRtbCkuDQoNCipOb3RlOiBJZiB5b3UgZG8gbm90IGhhdmUgdGhlIGBwYWxtZXJwZW5ndWluc2AgcGFja2FnZSBkb3dubG9hZGVkLCBqdXN0IGNsaWNrIG9uIHRoZSBgQ29kZWAgYm94IGJlbG93LCBhbmQgcnVuIHRoZSBjb2RlIHRoYXQgYXBwZWFyczoqDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUiLCBldmFsID0gRiwgZWNobyA9IFR9DQppbnN0YWxsLnBhY2thZ2VzKCJwYWxtZXJwZW5ndWlucyIpDQpgYGANCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBULCBlY2hvID0gRiwgaW5jbHVkZSA9IEZ9DQpsaWJyYXJ5KHBhbG1lcnBlbmd1aW5zKQ0KYGBgDQoNCk9wZW4gdXAgUlN0dWRpbywgYW5kIHJ1biB0aGUgZm9sbG93aW5nIGNvZGUgdG8gbG9hZCBhbmQgc3VtbWFyaXNlIHRoZSBgcGFsbWVycGVuZ3VpbnNgIHBhY2thZ2UuDQoNCipOb3RlIHRoYXQgdGhlIHBhY2thZ2UgaXMgY2FsbGVkIGBwYWxtZXJwZW5ndWluc2AsIGJ1dCBvbmNlIHRoaXMgaXMgbG9hZGVkLCB0aGUgYWN0dWFsIGRhdGEgdG8gYWNjZXNzIGluIFIgaXMgc3RvcmVkIGluIHRoZSBvYmplY3QgYHBlbmd1aW5zYC4qDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gRiwgZWNobyA9IFR9DQojIFRoaXMgY29kZSBsb2FkcyB0aGUgYHBhbG1lcnBlbmd1aW5zYCBwYWNrYWdlIGludG8geW91ciBjdXJyZW50IFIgd29ya2luZyBlbnZpcm9ubWVudC4NCmxpYnJhcnkocGFsbWVycGVuZ3VpbnMpDQpgYGANCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBGLCBlY2hvID0gVH0NCiMgVGhpcyBjb2RlIHN1bW1hcmlzZXMgdGhlIGRhdGEgaW4gdGhlIGBwYWxtZXJwZW5ndWluc2AgcGFja2FnZS4NCnN1bW1hcnkocGVuZ3VpbnMpDQpgYGANCg0KRG9uJ3Qgd29ycnkgdG9vIG11Y2ggYWJvdXQgdGhlIHZhbHVlcyBzaG93biBpbiB0aGUgc3VtbWFyeSB0YWJsZSAtIHRoZSBtYWluIHRoaW5ncyB0byBub3RlIGF0IHRoaXMgc3RhZ2UgYXJlIHRoZSBkaWZmZXJlbnQgdmFyaWFibGVzLCBuYW1lbHkgYHNwZWNpZXNgLCBgaXNsYW5kYCwgYGJpbGxfbGVuZ3RoX21tYCwgYGJpbGxfZGVwdGhfbW1gLCBgZmxpcHBlcl9sZW5ndGhfbW1gLCBgYm9keV9tYXNzX2dgLCBgc2V4YCBhbmQgYHllYXJgLg0KDQojIFBsb3RseSBTY2F0dGVyIFBsb3RzIHsjc2NhdHRlcn0gDQoNCkZyb20gb3VyIHN1bW1hcnkgdGFibGUsIHdlIGNhbiBzZWUgdGhhdCB0aGUgbWVhc3VyZW1lbnQgdmFyaWFibGVzIGZvciB0aGUgcGVuZ3VpbnMgaW5jbHVkZSBgYm9keV9tYXNzX2dgIGFuZCBgZmxpcHBlcl9sZW5ndGhfbW1gLg0KSXQgc2VlbXMgcmVhc29uYWJsZSB0byB0aGluayB0aGF0IHBlbmd1aW5zIHdpdGggbGFyZ2VyIGJvZHkgbWFzc2VzIG1pZ2h0IGFsc28gaGF2ZSBsb25nZXIgZmxpcHBlcnMuIA0KDQpUbyB2aXN1YWxpc2UgdGhpcywgYW5kIGNoZWNrIG91ciBhc3N1bXB0aW9uLCB3ZSBjb3VsZCB1c2UgYSAqKnNjYXR0ZXIgcGxvdCoqLiBUbyBjcmVhdGUgdGhpcyBzY2F0dGVyIHBsb3QsIHdlIHdpbGwgdXNlIHRoZSBgcGxvdGx5YCBwYWNrYWdlLCB3aGljaCBvZmZlcnMgc2V2ZXJhbCBiZW5lZml0cyBvdmVyIHVzaW5nIHRoZSBkZWZhdWx0IHBsb3R0aW5nIG9wdGlvbnMgaW4gUi4gRm9yIG1vcmUgZGV0YWlscyBvbiBgcGxvdGx5YCwgeW91IGNhbiByZWZlciB0byBbU2VjdGlvbiAzIG9mIHRoZSBEYXRhIFZpc3VhbGlzYXRpb24gaW4gUiBzdXBwbGVtZW50XShodHRwczovL2Jvb2tkb3duLm9yZy9yZWhrL3N0bTEwMDFfZHNtX3QxX2RhdGFfdmlzdWFsaXNhdGlvbl9pbl9yL3Bsb3RseS1jb21wdXRlci1sYWJzLTFiLmh0bWwpLg0KDQojIyANCg0KRmlyc3QsIGxldCdzIGxvYWQgdGhlIGBwbG90bHlgIHBhY2thZ2UuIFVzaW5nIG91ciBwcm9jZXNzIGluIFxAcmVmKHBlbmd1aW5zKSBhcyBhIGd1aWRlLCBsb2FkIHRoZSBgcGxvdGx5YCBwYWNrYWdlIGluIFIuIA0KKElmIHlvdSBkbyBub3QgaGF2ZSB0aGUgYHBsb3RseWAgIHBhY2thZ2UgZG93bmxvYWRlZCwgbWFrZSBzdXJlIHRvIGRvIHRoaXMgbm93IHRvbykuDQoNCipIaW50OiBDaGVjayB0aGUgY29kZSBjaHVuayBiZWxvdyBpZiB5b3UgYXJlIG5vdCBzdXJlIGhvdyB0byBwcm9jZWVkLioNCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBULCBlY2hvID0gRiwgaW5jbHVkZSA9IEZ9DQpsaWJyYXJ5KHBsb3RseSkNCmBgYA0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIiwgZXZhbCA9IEYsIGVjaG8gPSBUfQ0KaW5zdGFsbC5wYWNrYWdlcygicGxvdGx5IikNCmxpYnJhcnkocGxvdGx5KQ0KYGBgDQoNCiMjIHsjc2ltcGxlc2NhdHRlcn0NCg0KV2UgY2FuIGNyZWF0ZSBhIGBwbG90bHlgIHBsb3QgdXNpbmcgdGhlIGZ1bmN0aW9uIGBwbG90X2x5KClgLiBMZXQncyB0YWtlIGEgbG9vayBhdCB0aGUgdHlwaWNhbCBjb21wb3NpdGlvbiBvZiBhIGBwbG90bHlgIHBsb3Q6DQoNCiBgcGxvdF9uYW1lIDwtIHBsb3RfbHkoZGF0YSA9IC4uLiwgeCA9IH4gLi4uLCB5ID0gfiAuLi4pYA0KIA0KTGV0J3MgYnJlYWsgdGhpcyBkb3duLiANCg0KKiBGaXJzdGx5LCAodXNpbmcgdGhlIGFzc2lnbm1lbnQgb3BlcmF0b3IgYDwtYCkgd2UgYXNzaWduIGEgbmFtZSB0byBvdXIgcGxvdCAtIGhlcmUgd2UgaGF2ZSBjaG9zZW4gdGhlIGdlbmVyaWMgYHBsb3RfbmFtZWAuIA0KKiBOZXh0LCB3aXRoaW4gYHBsb3RseSgpYCwgd2Ugc3BlY2lmeSB0aGUgbWFpbiBhcmd1bWVudHMgb2YgdGhlIGZ1bmN0aW9uLiANCiogVGhlIGBkYXRhID0gLi4uYCBwYXJ0IHRlbGxzIFIgd2hhdCBkYXRhIHdlIGFyZSBhbmFseXNpbmcuIA0KKiBUaGUgYHggPSB+IC4uLmAgcGFydCB0ZWxscyBSIHdoaWNoIHZhcmlhYmxlIGluIG91ciBkYXRhIHNldCB0byBwbG90IG9uIHRoZSB4LWF4aXMgb2Ygb3VyIHBsb3QuDQoqIFRoZSBgeSA9IH4gLi4uYCBwYXJ0IHRlbGxzIFIgd2hpY2ggdmFyaWFibGUgaW4gb3VyIGRhdGEgc2V0IHRvIHBsb3Qgb24gdGhlIHktYXhpcyBvZiBvdXIgcGxvdC4NCiANCk5vdGUgdGhhdCB3ZSBzaW1wbHkgcmVwbGFjZSB0aGUgYC4uLmBzIHdpdGggd2hhdGV2ZXIgZGF0YSB3ZSBhcmUgdXNpbmcuDQoNClRoZSBjb2RlIGJlbG93IHdpbGwgY3JlYXRlIGEgc2ltcGxlIHNjYXR0ZXIgcGxvdCBvZiBgZmxpcHBlcl9sZW5ndGhfbW1gIHZlcnN1cyBgYm9keV9tYXNzX2dgLiBNYWtlIHN1cmUgdG8gaW5zcGVjdCB0aGlzIGNvZGUsIGFuZCBjaGVjayB0aGF0IHlvdSB1bmRlcnN0YW5kIGVhY2ggY29tcG9uZW50LiBTaW5jZSB3ZSBoYXZlIHNwZWNpZmllZCBvdXIgZGF0YSBzZXQgaXMgYHBlbmd1aW5zYCwgd2UgZG9uJ3QgdGhlbiBuZWVkIHRvIGRvIHRoaXMgd2hlbiBzcGVjaWZ5aW5nIG91ciBgeGAgYW5kIGB5YCBpbnB1dHMgLSB3ZSBjYW4gc2ltcGx5IHNwZWNpZnkgYW55IG9mIHRoZSB2YXJpYWJsZXMgY29udGFpbmVkIHdpdGhpbiB0aGlzIGRhdGEgc2V0Lg0KDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gRiwgZWNobyA9IFR9DQpwZW5ndWluc19zY2F0dGVyIDwtIHBsb3RfbHkoZGF0YSA9IHBlbmd1aW5zLCB4ID0gfmJvZHlfbWFzc19nLCB5ID0gfmZsaXBwZXJfbGVuZ3RoX21tKQ0KcGVuZ3VpbnNfc2NhdHRlcg0KYGBgDQoNCipOb3RlIHRoYXQgb25jZSB3ZSBhc3NpZ24gdGhlIHBsb3QgdG8gdGhlIG9iamVjdCBgcGVuZ3VpbnNfc2NhdHRlcmAsIHdlIHRoZW4gaGF2ZSB0byBydW4gdGhpcyBvYmplY3QgaW4gYSBzdWJzZXF1ZW50IGxpbmUsIGluIG9yZGVyIGZvciB0aGUgcGxvdCB0byBiZSByZW5kZXJlZC4qDQoNCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBULCBlY2hvID0gRiwgd2FybmluZyA9IEZ9DQpwZW5ndWluc19zY2F0dGVyIDwtIHBsb3RfbHkoZGF0YSA9IHBlbmd1aW5zLCB4ID0gfmJvZHlfbWFzc19nLCB5ID0gfmZsaXBwZXJfbGVuZ3RoX21tLCB0eXBlID0gInNjYXR0ZXIiLCBtb2RlID0gIm1hcmtlcnMiKQ0Kc3VwcHJlc3NNZXNzYWdlcyhwZW5ndWluc19zY2F0dGVyKQ0KYGBgDQoNCiMjDQoNCk9uZSBrZXkgYmVuZWZpdCBvZiBgcGxvdGx5YCBncmFwaHMgY29tcGFyZWQgdG8gYmFzZSBSIGdyYXBocywgaXMgdGhhdCB0aGUgYHBsb3RseWAgZ3JhcGhzIGFyZSBpbnRlcmFjdGl2ZSENCg0KTm90aWNlIGlzIHRoYXQgaWYgeW91IGhvdmVyIG92ZXIgdGhlIGRhdGEgaW4gdGhlIHNjYXR0ZXIgcGxvdCwgeW91IGNhbiBzZWUgdGhlIHNwZWNpZmljIGNvb3JkaW5hdGVzIG9mIGVhY2ggcG9pbnQuIA0KSWYgeW91IGxlZnQtY2xpY2sgYW5kIGRyYWcgeW91ciBjdXJzb3Igb3ZlciBhIHNlY3Rpb24gdG8gY3JlYXRlIGEgYm94LCB5b3UgY2FuIGFsc28gem9vbSBpbiBvbiBhIHBhcnRpY3VsYXIgc2VjdGlvbiBvZiB0aGUgcGxvdC4gSnVzdCBkb3VibGUgbGVmdC1jbGljayB0byB6b29tIGJhY2sgb3V0Lg0KDQojIyB7I3NjYXR0ZXJjb2xvdXJ9DQoNCkFzIHdlIHN1c3BlY3RlZCwgaXQgc2VlbXMgcXVpdGUgY2xlYXIgdGhhdCBhcyB0aGUgYm9keSBtYXNzIG9mIHBlbmd1aW5zIGluY3JlYXNlcywgc28gdG9vIGRvZXMgdGhlaXIgZmxpcHBlciBsZW5ndGguDQoNCkJ1dCBvdXIgZ3JhcGggaXMgcXVpdGUgYmFzaWMgYXQgdGhlIG1vbWVudCAtIHdlIGNhbiBkbyBiZXR0ZXIuIA0KDQpBbm90aGVyIGdyZWF0IGFzcGVjdCBvZiBwbG90bHkgZ3JhcGhzIGlzIHRoYXQgaXQgaXMgdmVyeSBlYXN5IHRvIGluY2x1ZGUgYSB0aGlyZCB2YXJpYWJsZSB3aGljaCBjYW4gaGVscCB0byBmdXJ0aGVyIGRpc3Rpbmd1aXNoIHRoZSBkYXRhIHBsb3R0ZWQgb24gdGhlIGB4YCBhbmQgYHlgIGF4ZXMuIFdlIGNhbiBkbyBzbyBieSBhZGRpbmcgdGhlIGFyZ3VtZW50IGBjb2xvciA9IH4uLi5gIHdpdGhpbiBgcGxvdGx5KClgLiANCg0KUGVyaGFwcyB0aGUgYGJvZHlfbWFzc19nYCBhbmQgYGZsaXBwZXJfbGVuZ3RoX21tYCAgb2YgdGhlIHBlbmd1aW5zIGlzIGFsc28gcmVsYXRlZCB0byB0aGVpciBgc2V4YD8NCkxldCdzIHRha2UgYSBsb29rIGF0IGhvdyBvdXIgc2NhdHRlciBwbG90IGNoYW5nZXMsIGlmIHdlIGRpc3Rpbmd1aXNoIGJldHdlZW4gbWFsZSBhbmQgZmVtYWxlIHBlbmd1aW5zLg0KDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gRiwgZWNobyA9IFR9DQpwZW5ndWluc19zY2F0dGVyMiA8LSBwbG90X2x5KGRhdGEgPSBwZW5ndWlucywgeCA9IH5ib2R5X21hc3NfZywgeSA9IH5mbGlwcGVyX2xlbmd0aF9tbSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gfnNleCkNCnBlbmd1aW5zX3NjYXR0ZXIyDQpgYGANCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBULCBlY2hvID0gRiwgd2FybmluZyA9IEYsIGZpZy5hbGlnbiA9ICJjZW50ZXIifQ0KcGVuZ3VpbnNfc2NhdHRlcjIgPC0gcGxvdF9seShkYXRhID0gcGVuZ3VpbnMsIHggPSB+Ym9keV9tYXNzX2csIHkgPSB+ZmxpcHBlcl9sZW5ndGhfbW0sIGNvbG9yID0gfnNleCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAic2NhdHRlciIsIG1vZGUgPSAibWFya2VycyIpDQpzdXBwcmVzc01lc3NhZ2VzKHBlbmd1aW5zX3NjYXR0ZXIyKQ0KYGBgDQoNClRoYXQncyBsb29raW5nIGEgYml0IG5pY2VyISBOb3cgd2UgY2FuIHNlZSB0aGF0IG1vc3Qgb2YgdGhlIHNtYWxsZXIgcGVuZ3VpbnMgYXJlIGZlbWFsZSwgYW5kIG1vc3Qgb2YgdGhlIGxhcmdlciBwZW5ndWlucyBhcmUgbWFsZS4gSWYgeW91IGhvdmVyIG92ZXIgdGhlIGRhdGEsIHlvdSdsbCBub3RpY2UgdGhhdCB0aGUgYHNleGAgaXMgbm93IHNob3duIGFsb25nc2lkZSB0aGUgY29vcmRpbmF0ZXMgb2YgZWFjaCBkYXRhIHBvaW50LiANCg0KV2UgYWxzbyBoYXZlIGEgaGVscGZ1bCBsZWdlbmQgaW4gdGhlIHRvcCByaWdodC4gVGhpcyBpcyBub3Qgb25seSB1c2VmdWwgYXMgYSBndWlkZSAtIHRyeSBjbGlja2luZyBvbiBvbmUgb2YgdGhlIGxhYmVscyBpbiB0aGUgbGVnZW5kLg0KDQojIyB7I3NjYXR0ZXJjb2xvdXJzfQ0KDQpXaGlsZSBvdXIgc2NhdHRlciBwbG90IGlzIGxvb2tpbmcgYmV0dGVyLCB0aGUgZGVmYXVsdCBjb2xvdXJzIGNob3NlbiB0byBkaXN0aW5ndWlzaCBiZXR3ZWVuIG1hbGUgYW5kIGZlbWFsZSBwZW5ndWlucyBhcmUgcXVpdGUgc2ltaWxhci4gUGVyaGFwcyB3ZSB3b3VsZCBsaWtlIG1vcmUgY29udHJhc3Q/DQoNClRvIHNwZWNpZnkgdGhlIHNldCBvZiBjb2xvdXJzIHRvIHVzZSBmb3IgdGhlIHBsb3QsIHdlIGNhbiBhZGQgdGhlIGFkZGl0aW9uYWwgYXJndW1lbnQgYGNvbG9ycyA9IC4uLmAgdG8gb3VyIGBwbG90X2x5YCBmdW5jdGlvbi4gVGhpcyBhcmd1bWVudCBhY2NlcHRzIGFueSB2YWxpZCBSIGNvbG91ciBjb2Rlcy4gVGFrZSBhIGxvb2sgYXQgW3RoaXMgcGRmXShodHRwOi8vd3d3LnN0YXQuY29sdW1iaWEuZWR1L350emhlbmcvZmlsZXMvUmNvbG9yLnBkZikgZm9yIGFuIG92ZXJ2aWV3IG9mIGRpZmZlcmVudCBjb2xvdXJzIHdlIGNhbiB1c2UgaW4gUi4NCg0KQ29tcGxldGUgYW5kIHRoZW4gcnVuIHRoZSBjb2RlIGJlbG93IHRvIGNoYW5nZSB0aGUgY29sb3VycyB5b3UgdXNlIGluIHlvdXIgc2NhdHRlciBwbG90LiANCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBGLCBlY2hvID0gVH0NCnBlbmd1aW5zX3NjYXR0ZXJfY29sb3VycyA8LSBwbG90X2x5KGRhdGEgPSBwZW5ndWlucywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB4ID0gfmJvZHlfbWFzc19nLCB5ID0gfmZsaXBwZXJfbGVuZ3RoX21tLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gfnNleCwgY29sb3JzID0gLi4uKQ0KcGVuZ3VpbnNfc2NhdHRlcl9jb2xvdXJzDQpgYGANCg0KKkhpbnQ6IFlvdSB3aWxsIG5lZWQgYSBjb21iaW5hdGlvbiBvZiB0d28gY29sb3Vycy4gQ2hlY2sgdGhlIGBDb2RlYCBib3ggYmVsb3cgaWYgeW91IGFyZSBzdHVjay4qDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUiLCBldmFsID0gRiwgZWNobyA9IFQsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRn0NCiMgSWYgeW91IGFyZSBzcGVjaWZ5aW5nIHNwZWNpZmljIGluZGl2aWR1YWwgY29sb3VycywgeW91IHdpbGwgbmVlZCB0byB1c2UgdGhlIGxheW91dCANCmNvbG9ycyA9IGMoIi4uLiIsICIuLi4iKS4NCmBgYA0KDQojIw0KDQpJZiB5b3UgZG8gbm90IHdhbnQgdG8gc3BlbmQgdG9vIG11Y2ggdGltZSBjdXN0b21pc2luZyB0aGUgY29sb3VycyB1c2VkIGluIHlvdXIgcGxvdHMsIHRoZXJlIGFyZSBwcmUtZXhpc3Rpbmcgc2V0cyBvZiBjb2xvdXJzIHlvdSBjYW4gdXNlLiBUcnkgc2V0dGluZyB5b3VyIGBjb2xvcnMgPS4uLmAgYXJndW1lbnQgaW4gXEByZWYoc2NhdHRlcmNvbG91cnMpIHNlcXVlbnRpYWxseSB0byBgY29sb3JzID0gIlNldDEiYCwgdGhlbiB0byBgY29sb3JzID0gIlNldDIiYCBhbmQgZmluYWxseSB0byBgY29sb3JzID0gIlNldDMiYC4gRG8gYW55IHBhcnRpY3VsYXIgc2V0cyBhcHBlYWwgdG8geW91Pw0KDQojIw0KDQpUaGVyZSBhcmUgbWFueSBkaWZmZXJlbnQgZGlzcGxheSBvcHRpb25zIGZvciBgcGxvdF9seWAgZ3JhcGhpY3MsIGFuZCBpZiB5b3UgdHJ5IHJ1bm5pbmcgdGhlIFIgY29tbWFuZHMNCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBGLCBlY2hvID0gVH0NCnBlbmd1aW5zX3NjYXR0ZXIyIDwtIHBsb3RfbHkoZGF0YSA9IHBlbmd1aW5zLCB4ID0gfmJvZHlfbWFzc19nLCB5ID0gfmZsaXBwZXJfbGVuZ3RoX21tLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSB+c2V4LCBjb2xvcnMgPSAiU2V0MSIpDQpwZW5ndWluc19zY2F0dGVyMg0KYGBgDQoNCnlvdSBtYXkgc2VlIHNvbWUgcmVkIGBXYXJuaW5nIG1lc3NhZ2VzYCBhcHBlYXIgaW4gdGhlIFIgQ29uc29sZS4gT2Z0ZW4sIHlvdSBkb24ndCBoYXZlIHRvIHdvcnJ5IGFib3V0IHRoZXNlLCBidXQgaWYgeW91IHdvdWxkIGxpa2UgdG8gbWluaW1pc2UgdGhlbSwgeW91IGNhbiBhZGQgdGhlIGZvbGxvd2luZyBhcmd1bWVudHMgdG8geW91ciBgcGxvdF9seWAgZnVuY3Rpb24uDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gRiwgZWNobyA9IFR9DQpwZW5ndWluc19zY2F0dGVyMiA8LSBwbG90X2x5KGRhdGEgPSBwZW5ndWlucywgeCA9IH5ib2R5X21hc3NfZywgeSA9IH5mbGlwcGVyX2xlbmd0aF9tbSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gfnNleCwgY29sb3JzID0gIlNldDEiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlID0gInNjYXR0ZXIiLCBtb2RlID0gIm1hcmtlcnMiKQ0KcGVuZ3VpbnNfc2NhdHRlcjINCmBgYA0KDQpIZXJlLCB3ZSBoYXZlIGluY2x1ZGVkIHRoZSBhZGRpdGlvbmFsIGFyZ3VtZW50cyBgdHlwZSA9IC4uLmAgYW5kIGBtb2RlID0gLi4uYC4NCg0KKiBXZSBzZXQgYHR5cGUgPSAic2NhdHRlciJgIHRvIGVuc3VyZSBvdXIgZGF0YSBpcyBwbG90dGVkIGFzIGEgc2NhdHRlciBwbG90Lg0KKiBXZSBzZXQgYG1vZGUgPSAibWFya2VycyJgIHRvIGVuc3VyZSB0aGF0IGVhY2ggb2Ygb3VyIGRhdGEgcG9pbnRzIGlzIHBsb3R0ZWQgaW5kaXZpZHVhbGx5Lg0KDQpUaGVzZSBhZGRpdGlvbmFsIGFyZ3VtZW50cyBhcmUgb2Z0ZW4gaGVscGZ1bCwgYXMgc29tZXRpbWVzIHdlIGxpa2UgdG8gaGF2ZSBhIGxpdHRsZSBtb3JlIGNvbnRyb2wgb3ZlciBob3cgb3VyIGRhdGEgaXMgcHJlc2VudGVkLg0KDQpZb3UnbGwgbm90aWNlIGhvd2V2ZXIgdGhhdCBpZiB0aGVzZSBjb21tYW5kcyBhcmUgb21pdHRlZCBmcm9tIHlvdXIgZnVuY3Rpb24sIFIgd2lsbCBqdXN0IHdvcmsgb3V0IHdoYXQgaXQgdGhpbmtzIGlzIHRoZSBvcHRpbWFsIHByZXNlbnRhdGlvbiBmb3JtYXQgKGhlbmNlIHRoZSB3YXJuaW5nIG1lc3NhZ2VzIGluZm9ybWluZyB1cyB3aGljaCBvcHRpb25zIFIgaGFzIHNlbGVjdGVkLCBzaW5jZSBzb21lIGRldGFpbHMgaGF2ZW4ndCBiZWVuIHVzZXItc3BlY2lmaWVkKS4NCg0KVGhpcyBpcyBvZnRlbiBmb3IgdGhlIGJlc3QgLSB0cnkgY2hhbmdpbmcgdGhlIGBtb2RlID0gIm1hcmtlcnMiYCBzZWN0aW9uIG9mIGNvZGUgdG8gYG1vZGUgPSJsaW5lcyJgIGFuZCB0aGVuIHJlLXJ1bm5pbmcgdGhlIHBsb3QuIFdoYXQgaGFwcGVucz8NCg0KIyMgeyNzY2F0dGVyc3ltYm9sfQ0KDQpTbyBmYXIsIHdlIGhhdmUgdHJlYXRlZCBhbGwgdGhlIHBlbmd1aW5zIGFzIG9uZSBsYXJnZSBncm91cCwgZGlmZmVyZW50aWF0ZWQgYnkgYHNleGAuIEhvd2V2ZXIsIHdlIGFjdHVhbGx5IGhhdmUgZGF0YSBmb3IgdGhyZWUgc2VwYXJhdGUgc3BlY2llcyBvZiBwZW5ndWluIC0gYEFkZWxpZWAsIGBDaGluc3RyYXBgLCBhbmQgYEdlbnRvb2AuDQoNCldlIGhhdmUgYWxyZWFkeSB1c2VkIGRpZmZlcmVudCBjb2xvdXJzIHRvIGRpZmZlcmVudGlhdGUgdGhlIG1hbGUgYW5kIGZlbWFsZSBwZW5ndWlucywgYnV0IHNvIGZhciBhbGwgdGhlIGRhdGEgcG9pbnRzIGFyZSB0aGUgc2FtZSBzeW1ib2wgLSBhIGRvdC4gV2UgY2FuIHVzZSB0aGUgYWRkaXRpb25hbCBhcmd1bWVudCBgc3ltYm9sID0gLi4uYCB3aXRoaW4gb3VyIGBwbG90X2x5YCBmdW5jdGlvbiB0byBmdXJ0aGVyIGltcHJvdmUgb3VyIGdyYXBoLCBhbmQgZGlzdGluZ3Vpc2ggYmV0d2VlbiB0aGUgZGlmZmVyZW50IHNwZWNpZXMgb2YgcGVuZ3Vpbi4gDQoNClRha2UgYSBsb29rIGF0IHRoZSBSIGNvZGUgYW5kIHJlc3VsdGFudCBncmFwaCBiZWxvdzoNCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBGLCBlY2hvID0gVH0NCnBlbmd1aW5zX3NjYXR0ZXIzIDwtIHBsb3RfbHkoZGF0YSA9IHBlbmd1aW5zLCB4ID0gfmJvZHlfbWFzc19nLCB5ID0gfmZsaXBwZXJfbGVuZ3RoX21tLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSB+c2V4LCBjb2xvcnMgPSAiU2V0MSIsIHN5bWJvbCA9IH5zcGVjaWVzLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZSA9ICJzY2F0dGVyIiwgbW9kZSA9ICJtYXJrZXJzIikNCnBlbmd1aW5zX3NjYXR0ZXIzDQpgYGANCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBULCBlY2hvID0gRiwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGLCBmaWcuYWxpZ24gPSAiY2VudGVyIn0NCnBlbmd1aW5zX3NjYXR0ZXIzIDwtIHBsb3RfbHkoZGF0YSA9IHJlbW92ZV9taXNzaW5nKHBlbmd1aW5zKSwgeCA9IH5ib2R5X21hc3NfZywgeSA9IH5mbGlwcGVyX2xlbmd0aF9tbSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gfnNleCwgY29sb3JzID0gIlNldDEiLCBzeW1ib2wgPSB+c3BlY2llcywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAic2NhdHRlciIsIG1vZGUgPSAibWFya2VycyIpDQoNCnBlbmd1aW5zX3NjYXR0ZXIzDQpgYGANCg0KR3JlYXQhIFRoaXMgaXMgbG9va2luZyBtdWNoIG1vcmUgaW5mb3JtYXRpdmUgdGhhbiBvdXIgaW5pdGlhbCBzY2F0dGVyIHBsb3QuIE5vdyBpdCdzIHF1aXRlIGNsZWFyIGZvciBpbnN0YW5jZSB0aGF0IHRoZSBtYWpvcml0eSBvZiBsYXJnZXIgcGVuZ3VpbnMgKGJvdGggbWFsZSBhbmQgZmVtYWxlKSBhcmUgb2YgdGhlIEdlbnRvbyBzcGVjaWVzLCB3aGljaCB3ZSBjb3VsZG4ndCBkaXNjZXJuIGZyb20gb3VyIHByZXZpb3VzIHZlcnNpb24gb2YgdGhlIHNjYXR0ZXIgcGxvdC4NCg0KIyMNCg0KSnVzdCBhcyBSIGhhcyBtYW55IGNvbG91ciBvcHRpb25zIGF2YWlsYWJsZSwgc28gdG9vIGFyZSB0aGVyZSBtYW55IHN5bWJvbCBvcHRpb25zIGF2YWlsYWJsZS4gU2luY2Ugd2UgaGF2ZSBub3Qgc3BlY2lmaWVkIHdoaWNoIHNwZWNpZmljIHN5bWJvbHMgdG8gdXNlIGluIFxAcmVmKHNjYXR0ZXJzeW1ib2wpLCBSIGhhcyB1c2VkIHRoZSBmaXJzdCBkZWZhdWx0IDMuDQoNCldlIHVzZWQgYGNvbG9ycyA9IC4uLmAgdG8gbW9kaWZ5IG91ciBgY29sb3IgPSAuLi5gIHNwZWNpZmljYXRpb24sIGFuZCBzaW1pbGFybHksIHdlIGNhbiB1c2UgYHN5bWJvbHMgPSAuLi5gIHRvIG1vZGlmeSBvdXIgYHN5bWJvbCA9IC4uLmAgc3BlY2lmaWNhdGlvbi4gDQoNClRoZXJlIGFyZSAyNiBkaWZmZXJlbnQgYmFzZSBSIHN5bWJvbHMgeW91IGNhbiBjaG9vc2UgZnJvbSAtIHRoZXNlIGNhbiBiZSBzcGVjaWZpZWQgZWl0aGVyIGJ5IG51bWJlciwgb3IgYnkgbmFtZS4gU29tZSBvZiB0aGUgbmFtZXMgYXJlIHF1aXRlIGxvbmcsIGUuZy4gYCJmaWxsZWQgdHJpYW5nbGUgcG9pbnQtdXAiYCwgc28gaXQgaXMgb2Z0ZW4gZWFzaWVyIHRvIHVzZSBudW1iZXJzLiBIb3dldmVyLCBzb21lIG5hbWVzIGFyZSBlYXN5IHRvIHJlbWVtYmVyIC0gdGFrZSBhIGxvb2sgYXQgdGhlIHRhYmxlIGJlbG93Lg0KDQpgYGB7ciwgZWNobz1GLCBsYWJlbCA9ICJ0YWJsZSJ9DQpyb3cwIDwtIGMoIioqTnVtYmVyKioiLCAiKipOYW1lKioiKQ0Kcm93MSA8LSBjKDAsICJzcXVhcmUiKQ0Kcm93MiA8LSBjKDEsICJjaXJjbGUiKQ0Kcm93MyA8LSBjKDIsICJ0cmlhbmdsZSBwb2ludCB1cCIpDQpyb3c0IDwtIGMoMywgInBsdXMiKQ0Kcm93NSA8LSBjKDQsICJjcm9zcyIpDQpyb3c2IDwtIGMoNSwgImRpYW1vbmQiKQ0Kcm93NyA8LSBjKDgsICJzdGFyIikgDQptYXQgPC0gbWF0cml4KGMocm93MCwgcm93MSwgcm93Miwgcm93Mywgcm93NCwgcm93NSwgcm93Niwgcm93NyksIG5yb3cgPSA4LCBieXJvdyA9IFRSVUUpDQoNCmtuaXRyOjprYWJsZSgNCiAgbWF0LCANCiAgYm9va3RhYnMgPSBGQUxTRSwNCiAgY2FwdGlvbiA9ICdTeW1ib2wgT3B0aW9ucycNCikNCmBgYA0KDQp1c2luZyBcQHJlZih0YWI6dGFibGUpIGFuZCB0aGUgYHN5bWJvbHMgPSAuLi5gIGFyZ3VtZW50LCBjaGFuZ2UgdGhlIHN5bWJvbHMgdXNlZCBpbiB0aGUgYHBlbmd1aW5zX3NjYXR0ZXIzYCBzY2F0dGVyIHBsb3QgY3JlYXRlZCBpbiBcQHJlZihzY2F0dGVyc3ltYm9sKS4NCg0KKkhpbnQ6IElmIHlvdSBhcmUgdXNpbmcgc3ltYm9sIG5hbWVzLCBhbmQgeW91ciBjb2RlIGlzbid0IHdvcmtpbmcsIGNoZWNrIHRoZSBjb2RlIGNodW5rIGJlbG93LioNCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSIsIGV2YWwgPSBGLCBlY2hvID0gVH0NCiMgTm90ZSB0aGF0IGp1c3QgbGlrZSBmb3IgdGhlIGNvbG91cnMgYXJndW1lbnQsIGlmIHlvdSBhcmUgdXNpbmcgd29yZHMsIA0KIyB0aGVzZSBuZWVkIHRvIGJlIHN1cnJvdW5kZWQgd2l0aCBxdW90YXRpb24gbWFya3MsDQojIGUuZy4gInNxdWFyZSIsIG9yICdzcXVhcmUnIHdpbGwgd29yaywgYnV0IHNxdWFyZSB3aWxsIG5vdA0KYGBgDQoNCiMjDQoNCkFzIGEgZmluYWwgdG91Y2gsIHlvdSBtYXkgYWxzbyBsaWtlIHRvIGNoYW5nZSB0aGUgc2l6ZSBvZiB0aGUgc3ltYm9scyBpbiB5b3VyIHNjYXR0ZXIgcGxvdC4gDQpUbyBkbyBzbywgd2UgY2FuIGluY2x1ZGUgdGhlIGBtYXJrZXIgPSAuLi5gIGFyZ3VtZW50IGluIG91ciBgcGxvdF9seWAgZnVuY3Rpb24uDQoNClRoaXMgaXMgYSBsaXR0bGUgbW9yZSBjb21wbGljYXRlZCB0byB1c2UgdGhhbiBvdXIgcHJldmlvdXMgYXJndW1lbnRzLCBhcyBtdWx0aXBsZSBzcGVjaWZpY2F0aW9ucyBjYW4gYmUgbWFkZSB3aXRoaW4gdGhpcyBhcmd1bWVudC4gQXMgYSByZXN1bHQsIHdlIHVzZSB0aGUgZm9ybWF0IGBtYXJrZXIgPSBsaXN0KC4uLilgLiBXaXRoaW4gdGhlIGBsaXN0KClgIGZ1bmN0aW9uLCB3ZSBjYW4gaW5jbHVkZSBtdWx0aXBsZSBzcGVjaWZpY2F0aW9ucyB3aGljaCBhbGwgcGVydGFpbiB0byB0aGUgYG1hcmtlcmAgYXJndW1lbnQuDQoNClRvIGNoYW5nZSB0aGUgc2l6ZSBvZiB0aGUgc3ltYm9scywgd2UgdXNlIHRoZSBhcHByb3ByaWF0ZWx5IG5hbWVkIGBzaXplID0gYCBhcmd1bWVudCwgd2l0aGluIHRoZSBgbGlzdCgpYCBmdW5jdGlvbi4NCkFzIGEgcmVzdWx0LCBpZiB3ZSB3YW50IHRvIGNoYW5nZSB0aGUgZGVmYXVsdCBtYXJrZXIgc2l6ZSAoNikgdG8gYmUgYSBsaXR0bGUgbGFyZ2VyLCB3ZSBjb3VsZCBpbmNsdWRlIHRoZSBhcmd1bWVudA0KYG1hcmtlciA9IGxpc3Qoc2l6ZSA9IDgpYCB3aXRoaW4gb3VyIGBwbG90X2x5YCBmdW5jdGlvbiBmb3Igb3VyIGBwZW5ndWluc19zY2F0dGVyM2Agc2NhdHRlciBwbG90IGNyZWF0ZWQgaW4gXEByZWYoc2NhdHRlcnN5bWJvbCkuIA0KDQpUcnkgdGhpcyBub3csIG9ic2VydmUgdGhlIGNoYW5nZXMsIGFuZCB0aGVuIHRyeSBpbmNyZWFzaW5nIGFuZCBkZWNyZWFzaW5nIHRoZSBtYXJrZXIgc2l6ZS4NCg0KDQojIENyZWF0aW5nIHlvdXIgb3duIFBsb3RseSBTY2F0dGVyIFBsb3QgeyNzY2F0dGVycGVyc29uYWx9DQoNCk5vdyB0aGF0IHdlIGhhdmUgY292ZXJlZCB0aGUgYmFzaWNzIG9mIGNyZWF0aW5nIGBwbG90bHlgIHNjYXR0ZXIgcGxvdHMgaW4gXEByZWYoc2NhdHRlciksIGl0J3MgdGltZSBmb3IgeW91IHRvIGNyZWF0ZSB5b3VyIG93biB1c2luZyB0aGUgYHBlbmd1aW5zYCBkYXRhIHNldC4NCg0KIyMNCg0KVG8gYmVnaW4sIHRyeSBjcmVhdGluZyBhIHNpbXBsZSBgcGxvdGx5YCBzY2F0dGVyIHBsb3Qgb2YgYGJpbGxfbGVuZ3RoX21tYCB2ZXJzdXMgYGJvZHlfbWFzc19nYC4NCg0KKklmIHlvdSdyZSBub3Qgc3VyZSB0aGF0IHlvdSdyZSBvbiB0aGUgcmlnaHQgdHJhY2ssIHJlZmVyIGJhY2sgdG8gXEByZWYoc2ltcGxlc2NhdHRlciksIGFuZC9vciBjaGVjayB0aGUgY29kZSBiZWxvdzoqDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUiLCBldmFsID0gRiwgZWNobyA9IFQsIGZpZy5hbGlnbiA9ICJjZW50ZXIifQ0KcGVuZ3VpbnNfc2NhdHRlcl9uZXcgPC0gcGxvdF9seShkYXRhID0gcGVuZ3VpbnMsIHggPSB+Ym9keV9tYXNzX2csIHkgPSB+Li4uDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAic2NhdHRlciIsIG1vZGUgPSAibWFya2VycyIpDQpwZW5ndWluc19zY2F0dGVyX25ldw0KYGBgDQoNCiMjDQoNCk9uY2UgeW91IGFyZSBoYXBweSB3aXRoIHlvdXIgaW5pdGlhbCBzY2F0dGVyIHBsb3QsIHRyeSB1c2luZyB0aGUgYGNvbG9yID0gfmAgYXJndW1lbnQgdG8gZGlmZmVyZW50aWF0ZSB0aGUgZGF0YSBpbiB5b3VyIHBsb3QgYnkgYGlzbGFuZGAuIERvIHlvdSBub3RpY2UgYW55IHBhdHRlcm5zPw0KDQoqWW91IGNhbiByZWZlciBiYWNrIHRvIFxAcmVmKHNjYXR0ZXJjb2xvdXIpIGlmIHlvdSBhcmUgbm90IHN1cmUgaG93IHRvIHByb2NlZWQuKg0KDQojIw0KDQpOZXh0LCB1c2UgdGhlIGBzeW1ib2wgPSB+YCBhcmd1bWVudCB0byBzaG93IGRpZmZlcmVudCBzeW1ib2xzIGZvciBlYWNoIGBzcGVjaWVzYCBpbiB5b3VyIHBsb3QuDQoNCiMjDQoNClRvIGZpbmlzaCBvZmYgeW91ciBwbG90LCBjaGFuZ2UgdGhlIHN5bWJvbHMgaW4geW91ciBwbG90LCBhbmQgaW5jcmVhc2UgdGhlIG1hcmtlciBzaXplIG9mIHlvdXIgc3ltYm9scyBzbGlnaHRseS4NCg0KIyMNCg0KRG9lcyBpdCBzZWVtIGxpa2UgcGVuZ3VpbnMgbGl2aW5nIG9uIGRpZmZlcmVudCBpc2xhbmRzIGhhdmUgbm90aWNlYWJseSBkaWZmZXJlbnQgYGJvZHlfbWFzc19nYCBvciBgYmlsbF9sZW5ndGhfbW1gIG1lYXN1cmVtZW50cz8NCg0KDQojIE1peGVkIFN1YnBsb3RzIHsjc3VicGxvdHN9DQoNClJlY2FsbCBmcm9tIG91ciBbZmlyc3QgRGF0YSBTY2llbmNlIENvbXB1dGVyIExhYl0oaHR0cHM6Ly9ycHVicy5jb20vTFRVX1NUTTEwMDEvRFNNQ0wxX1MpIGhvdyB3ZSBjcmVhdGVkIHNvbWUgaGlzdG9ncmFtcyBmb3Igb3VyIGBwYWxtZXJwZW5ndWluc2AgZGF0YSBzZXQuIFNvbWUgb2YgdGhlIGNvZGUgdXNlZCBmb3IgdGhhdCBsYWIgaXMgcmVwcm9kdWNlZCBiZWxvdzoNCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBULCBlY2hvID0gVCwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGLCBmaWcuYWxpZ24gPSAiY2VudGVyIn0NCnBlbmd1aW5faGlzdCA8LSBwbG90X2x5KGRhdGEgPSBwZW5ndWlucywgeCA9IH5ib2R5X21hc3NfZywgY29sb3IgPSB+aXNsYW5kLCB0eXBlID0gImhpc3RvZ3JhbSIsIGFscGhhID0gMC42KQ0KDQpwZW5ndWluX2hpc3QgPC0gcGVuZ3Vpbl9oaXN0ICU+JSBsYXlvdXQoeWF4aXMgPSBsaXN0KHRpdGxlID0gJ2NvdW50JyksIGJhcm1vZGUgPSJvdmVybGF5IikNCnBlbmd1aW5faGlzdA0KYGBgDQoNClN1cHBvc2UgdGhhdCB3ZSB3b3VsZCBsaWtlIHRvIHByZXNlbnQgYWxsIG91ciBgcGFsbWVycGVuZ3VpbnNgIGRhdGEgdmlzdWFsaXNhdGlvbnMgdG9nZXRoZXIuIFdlIGNhbiBkbyB0aGlzIHVzaW5nIHRoZSBgc3VicGxvdGAgZnVuY3Rpb24uDQoNCiMjIHsjc3VicGxvdHdhbGt0aHJvdWdofQ0KDQpUYWtlIGEgbG9vayBhdCB0aGUgUiBjb2RlIGJlbG93Og0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IFQsIGVjaG8gPSBULCB3YXJuaW5nID0gRiwgbWVzc2FnZSA9IEZ9DQpwZW5ndWluX2NvbWJpbmVkX3Bsb3RzIDwtIHN1YnBsb3QocGVuZ3VpbnNfc2NhdHRlcjMsIHBlbmd1aW5faGlzdCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbnJvd3MgPSAyLCBtYXJnaW4gPSAwLjA1KSANCnBlbmd1aW5fY29tYmluZWRfcGxvdHMgPC0gcGVuZ3Vpbl9jb21iaW5lZF9wbG90cyAlPiUgDQogICAgICAgICAgICAgICAgICAgICAgICAgIGxheW91dCh0aXRsZSA9ICJQYWxtZXIgUGVuZ3VpbiBEYXRhIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHhheGlzID0gbGlzdCh0aXRsZSA9ICdib2R5X21hc3NfZycpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHlheGlzID0gbGlzdCh0aXRsZSA9ICJmbGlwcGVyX2xlbmd0aF9tbSIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeGF4aXMyID0gbGlzdCh0aXRsZSA9ICdib2R5X21hc3NfZycpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHlheGlzMiA9IGxpc3QodGl0bGUgPSAiY291bnQiKSkNCmBgYA0KDQpOb3RlIHRoYXQgaGVyZToNCg0KKiBXZSBhcmUgdXNpbmcgdGhlIGBzdWJwbG90YCBjb21tYW5kIHRvIHBsb3QgdGhlIGBwZW5ndWluc19zY2F0dGVyM2AgYW5kIGBwZW5ndWluX2hpc3RgIHBsb3RzIHRvZ2V0aGVyLg0KKiBUaGUgYG5yb3dzID0gMmAgYXJndW1lbnQgdGVsbHMgUiB0byBwcm9kdWNlIHRoZXNlIHBsb3RzIGluIDIgcm93cy4NCiogVGhlIGBtYXJnaW4gPSAwLjA1YCBhcmd1bWVudCB0ZWxscyBSIHRvIGxlYXZlIGEgc21hbGwgbWFyZ2luIGJldHdlZW4gdGhlIHR3byBwbG90cy4NCiogVGhlIHN1YnNlcXVlbnQgbGluZXMgb2YgY29kZSBhcmUgdXNlZCB0byBhZGQgYSB0aXRsZSB0byBvdXIgc2VsZWN0aW9uIG9mIHBsb3RzLCBhbmQgYWRkIGF4ZXMgbGFiZWxzIHRvIHRoZSBwbG90cyAtIG5vdGUgdGhhdCB3ZSB1c2UgYHhheGlzYCB0byBkZWZpbmUgdGhlIHgtYXhpcyBsYWJlbCBmb3IgdGhlIGZpcnN0IHBsb3QsIGFuZCBgeGF4aXMyYCB0byBkZWZpbmUgdGhlIHgtYXhpcyBsYWJlbCBmb3IgdGhlIHNlY29uZCBwbG90IChhbmQgc2ltaWxhcmx5IGZvciB0aGUgeS1heGVzKS4NCg0KV2hlbiB3ZSBub3cgcnVuIHRoaXMgb2JqZWN0IGBwZW5ndWluX2NvbWJpbmVkX3Bsb3RzYCwgd2Ugb2J0YWluIHRoZSBmb2xsb3dpbmc6DQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gVCwgZWNobyA9IFQsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRiwgZmlnLmRpbSA9IGMoMTAsIDgpLCBmaWcuYWxpZ24gPSAiY2VudGVyIn0NCnBlbmd1aW5fY29tYmluZWRfcGxvdHMNCmBgYA0KDQpOb3RlIHRoYXQgdGhlIHR3byBwbG90cyBhcmUgc3RpbGwgd2hvbGVseSBpbnRlcmFjdGl2ZS4gVGhlIGxlZ2VuZHMgaGF2ZSBiZWVuIGNvbWJpbmVkLCBhbmQgY2FuIGJlIHVzZWQgdG8gZmlsdGVyIHRoZSBpbmRpdmlkdWFsIHBsb3RzLg0KDQpXaGlsZSB3ZSBoYXZlIG9ubHkgY29tYmluZWQgdHdvIHBsb3RzIGhlcmUsIHRoZSBgc3VicGxvdGAgZnVuY3Rpb24gY2FuIGJlIHVzZWQgdG8gcHJlc2VudCBzZXZlcmFsIHBsb3RzIHRvZ2V0aGVyLCB3aGljaCBjYW4gYmUgcGFydGljdWxhcmx5IGluZm9ybWF0aXZlIHdoZW4geW91IHdvdWxkIGxpa2UgdG8gZGlzcGxheSBtdWx0aXBsZSBhc3BlY3RzIG9mIHlvdXIgZGF0YSBzaW11bHRhbmVvdXNseS4NClRoZSBvbmx5IG1ham9yIGRvd25zaWRlIG9mIHByZXNlbnRpbmcgcGxvdHMgdG9nZXRoZXIgdXNpbmcgYHN1YnBsb3RgIGlzIHRoYXQgdGhlaXIgYXhlcyBsYWJlbHMgYXJlIHJlbW92ZWQgYnkgZGVmYXVsdCwgYW5kIG11c3QgYmUgcmVzcGVjaWZpZWQsIGFzIGFib3ZlLg0KDQojIyANCg0KVXNpbmcgdGhlIGluZm9ybWF0aW9uIGZyb20gXEByZWYoc3VicGxvdHdhbGt0aHJvdWdoKSwgdHJ5IHRvIGNvbWJpbmUgdGhlIHNjYXR0ZXIgcGxvdCB5b3UgcHJvZHVjZWQgaW4gXEByZWYoc2NhdHRlcnBlcnNvbmFsKSB3aXRoIHRoZSBoaXN0b2dyYW0gc2hvd24gYWJvdmUgYXQgdGhlIHN0YXJ0IG9mIFxAcmVmKHN1YnBsb3RzKS4NCg0KKkhpbnQ6IFlvdSBkb24ndCBuZWVkIHRvIHdyaXRlIGFueSBjb2RlIGZvciB0aGUgaGlzdG9ncmFtLCB5b3UgY2FuIHNpbXBseSB1c2UgdGhlIFIgY29kZSBzaG93biBhdCB0aGUgc3RhcnQgb2YgXEByZWYoc3VicGxvdHMpLioNCg0KPGJyPg0KDQojIyMjIEdyZWF0IGpvYiwgdGhhdCdzIGV2ZXJ5dGhpbmcgZm9yIHRvZGF5ISAjIyMjIHstfQ0KDQpIb3BlZnVsbHkgeW91IG5vdyBmZWVsIGNvbmZpZGVudCBjcmVhdGluZyBwbG90bHkgc2NhdHRlciBwbG90cy4gRG9uJ3Qgd29ycnkgaWYgc29tZSBvZiB0aGUgY29kZSBzZWVtcyBkaWZmaWN1bHQgYXQgdGhlIG1vbWVudCAtIHdlIGFyZSBvbmx5IGF0IHRoZSBzZWNvbmQgbGFiLCBhbmQgd2Ugd2lsbCBoYXZlIHBsZW50eSBvZiB0aW1lIHRvIHByYWN0aWNlIGFuZCBpbXByb3ZlIGFzIHRoZSBzZW1lc3RlciBwcm9ncmVzc2VzLg0KDQpCZWZvcmUgeW91IGZpbmlzaCB1cCwgbWFrZSBzdXJlIHRvIHNhdmUgeW91ciBzY3JpcHQgZmlsZSBzb21ld2hlcmUgc2FmZSAtIGl0IG1pZ2h0IGNvbWUgaW4gaGFuZHkgbGF0ZXIgb24uIA0KDQo8YnI+DQoNCiMgUmVmZXJlbmNlcyB7LSAjUmVmfQ0KPGRpdiBpZD0icmVmcyI+PC9kaXY+DQoNCjxicj4NCg0KPGZvbnQgY29sb3IgPSAiZ3JleSI+DQpUaGVzZSBub3RlcyBoYXZlIGJlZW4gcHJlcGFyZWQgYnkgUnVwZXJ0IEt1dmVrZS4gVGhlIGNvcHlyaWdodCBmb3IgdGhlIG1hdGVyaWFsIGluIHRoZXNlIG5vdGVzIHJlc2lkZXMgd2l0aCB0aGUgYXV0aG9yIG5hbWVkIGFib3ZlLCB3aXRoIHRoZSBEZXBhcnRtZW50IG9mIE1hdGhlbWF0aWNhbCBhbmQgUGh5c2ljYWwgU2NpZW5jZXMgYW5kIHdpdGggTGEgVHJvYmUgVW5pdmVyc2l0eS4gQ29weXJpZ2h0IGluIHRoaXMgd29yayBpcyB2ZXN0ZWQgaW4gTGEgVHJvYmUgVW5pdmVyc2l0eSBpbmNsdWRpbmcgYWxsIExhIFRyb2JlIFVuaXZlcnNpdHkgYnJhbmRpbmcgYW5kIG5hbWluZy4gVW5sZXNzIG90aGVyd2lzZSBzdGF0ZWQsIG1hdGVyaWFsIHdpdGhpbiB0aGlzIHdvcmsgaXMgbGljZW5zZWQgdW5kZXIgYSBDcmVhdGl2ZSBDb21tb25zIEF0dHJpYnV0aW9uLU5vbiBDb21tZXJjaWFsLU5vbiBEZXJpdmF0aXZlcyBMaWNlbnNlIA0KPGEgaHJlZiA9ICJodHRwczovL2NyZWF0aXZlY29tbW9ucy5vcmcvbGljZW5zZXMvYnktbmMtbmQvNC4wL0NDIiB0YXJnZXQ9Il9ibGFuayI+IEJZLU5DLU5ELiA8L2E+DQo8L2ZvbnQ+