Data Science Module
Topic 3B: Data Visualisation III
Welcome to the third Data Science computer lab.
Last week, in the Data Science computer lab 2, we familiarised ourselves with the plotly
function, and made some informative and interactive plots, using the palmerpenguins
R data set (Horst, Hill, and Gorman 2020).
This week, we will focus on further developing our skills with plotly
(Sievert 2020), and cover adding custom controls and animations to our plotly
plots. The coding in this lab is a little more intense than in previous weeks, but we will take our time, and go through each of the steps slowly.
Before you begin this lab, make sure you have read over Section 4 of the Data Visualisation in R supplement on piping, and Section 5 of the Data Visualisation in R supplement on adding buttons to plotly
graphs.
By the end of this lab, you should understand piping, be able to create plotly
graphs with customized sliders and dropdown buttons, and have a decent idea of how to animate plotly
graphs.
Preparation
Before we begin our work, we will need to carry out some initial preparations.
To begin, we will need to load all the requisite packages.
By now, you should have the palmerpenguins
and plotly
packages installed on your system.
Open up RStudio and load these packages now.
If for some reason you do not have one or both of these packages installed, just click the Code
button below for the relevant code you will need to run.
install.packages("palmerpenguins")
install.packages("plotly")
If you need a quick refresher on how to load packages in R, just click the Code
button below.
library(palmerpenguins)
library(plotly)
Piping
Recall from Section 4.1 of the Data Visualisation in R supplement that the pipe operator can be used to chain together a sequence of operations in R, in an intuitive manner which is typically easier to read than alternative methods. Piping can be used to add additional details to existing objects, without the need to define new objects.
Let’s take a look at a simple example.
Recall in section 2 of the Data Science computer lab 2 that we created a simple scatter plot using the body_mass_g
and flipper_length_mm
variables from the penguins
data set. We also used different colours to distinguish between male and female penguins. This code is reproduced below:
penguins_scatter <- plot_ly(data = penguins,
x = ~body_mass_g, y = ~flipper_length_mm,
color = ~sex, colors = "Set1",
type = "scatter", mode = "markers")
When we hover over a point in our scatter plot, we see the flipper length, body mass, and sex details for that point. This is great, but we are missing one important piece of information - the species of penguin! Fortunately, it is straightforward to add this information to the hover text. We can do this by including the argument text = ~species
in our code, in a similar way to how we have used color = ~sex
to colour the points.
Update your penguins_scatter
plot with this addition now, and hover over some points to check that your code has worked as intended.
Suppose that we would like to add a title to the penguins_scatter
plot above.
Instead of rewriting our plot_ly
function in 2.2 and assigning the output to a new object (e.g. penguins_scatter2
), we could use the pipe operator to add this information to our penguins_scatter
plot.
The code below does just this:
penguins_scatter %>% layout(title = "Scatter Plot of Penguin Data")
We can also add a title to our legend. This can often help to make our graphs more informative.
Try running the code below, and check the result:
penguins_scatter %>% layout(title = "Scatter Plot of Penguin Data",
legend=list(title=list(text='Sex')))
In the 2.4 code, we were able to add details to multiple components of our plot, via the layout
function. Generally, when we make changes to plotly
plots via piping, we are making changes to the layout, rather than the core data being visualised.
Within the layout
function, we have used the argument title
(the function of which is to, rather appropriately, change the title). This is one of many possible arguments - some you will learn as we develop our understanding of plotly
, and some you may never use, as they are quite context specific. Typically though, the names of the arguments are clear and easy to remember - for instance, legend
allows us to change details in the legend.
To conclude this example, suppose that we would like to rename our plot’s x-axis and y-axis. The default names are ok, but perhaps we would like something a little different. Take a look at the code below, and try filling in the missing details (denoted by the …s) for the yaxis:
penguins_scatter %>% layout(xaxis = list(title = "Penguin Body Mass (grams)"),
...)
)
Make sure to run your code to verify it is working as intended.
Hint: If you would like to check your work, or are not quite sure if you are on the right track, just click the Code
button below.
# An example for the yaxis code is shown below
penguins_scatter %>% layout(xaxis = list(title = "Penguin Body Mass (grams)"),
yaxis = list(title = "Penguin Flipper Length (mm)")
)
Notice that we have included the list
function within our layout
function coding.
The xaxis
and yaxis
arguments can both take several settings - for example, we could change the x-axis title, and font size. This is typically the case for layout
arguments (the title in 2.3 was an exception).
Therefore, please keep in mind that generally speaking, in the context of our plotly
graphs, when dealing with the layout
function we need to use the list
function before specifying our desired changes to layout
arguments.
As a final note, it’s worth pointing out that our main title from 2.3 has disappeared in our new plot. This is because we did not assign our enhanced plot to a new object. When we use piping, we are not modifying the original object, but rather are carrying out operations on/with it. Therefore any changes we implement are not saved to the original object.
Adding Range Sliders to Plotly plots
Now that we have discussed the pipe operator and the layout
function, let’s consider adding some fancier elements to our plotly
graphs. First, let’s take a look at adding a range slider to our plot.
Suppose that we would like to add a range slider to our x-axis. A range slider can be used to dynamically select a subsection of our plot, in a similar, but more controlled way to left-clicking and dragging a box over our plot to zoom in on a section.
We can add a range slider to the x-axis of our plot using the function rangeslider()
.
Use the pipe operator to add a range slider to our scatter plot from 2.2.
If your code has worked, you should obtain the plot shown below - try left-clicking and dragging the bars on the slider endpoints.
Hint: You can always check the Code
box below if your code is not working.
penguins_scatter %>% rangeslider()
# Not too scary so far!
Initially, adding a range slider to your plot might have seemed very difficult.
However, as you can see, despite being an impressive addition it’s actually very straightforward to implement.
Of course, there are various additional adjustments that we could make to our range slider (and we will come back to range sliders later on in the semester), but for now, this one line of code is sufficient for our purposes.
Creating animated Plotly plots
Another (even more) impressive addition we can make to our plot is to turn it into an animation.
You might recall that one of the variables in the penguins
data set which we haven’t really considered so far is the year
variable - namely, we have penguin data recorded for the years 2007, 2008 and 2009.
Suppose we would like to see how the body_mass_g
and bill_length_mm
values of the male and female penguins changes over the years. We already have this information stored away in the penguins
data set, but we haven’t visualised it yet.
Could we somehow modify our scatter plot to show data for each year, and dynamically switch between years on command? Is such a thing even possible? Why, with plotly
, yes it is!
Adding animations to a plotly
plot is surprisingly easy, but we need to ensure that our data and code is set up properly. Fortunately, in this instance the penguins
data set which we are using already contains the information we would like to use for our animation.
The argument we will use to turn our scatter plot into an animated plot is frame =
. That’s it.
We need to include this inside our plotly()
function, in a similar fashion to how we use x=
and y=
when assigning the data for our x
and y
variables.
Try using the frame =
argument to add the year
variable into our scatter plot from 2.2. You may want to assign the new plot to a new object - e.g. penguins_scatter_anim
.
Your end result should look like the plot below:
If you haven’t already, try clicking on the Play
button, to watch the animation unfold.
If it’s a little fast, you can also click and drag the circle in the slider to change between years.
Next, instead of using the year
variable, create a scatter plot animation that cycles through the different species
of penguin in the penguins
data set. Change your hover text from showing the species of penguin, to showing the year the data was recorded.
What do you notice about the different species?
Adding buttons to Plotly plots
So far, hopefully the coding in this lab has not been too intense. That’s about to change.
In this final section, we’ll look at how to add buttons to our plotly
plot, which (when clicked) will allow us to shift between different presentations of our data. Specifically, we’ll add buttons that allow us to shift between viewing our penguins
data as a scatter plot, and as a histogram.
Please note - before you begin this section, make sure you have read through Section 5 of the Data Visualisation in R supplement. It may also be helpful to have this open in a separate tab, so that you can refer to it as you work through this section.
Sometimes, adding buttons to plotly
plots that include additional hover text can lead to display errors. Therefore, before continuing, please re-run the R code you used in 2.1 (reproduced in the code chunk below for your convenience):
penguins_scatter <- plot_ly(data = penguins,
x = ~body_mass_g, y = ~flipper_length_mm,
color = ~sex, colors = "Set1",
type = "scatter", mode = "markers")
Let’s take a step back, and consider some smaller modifications we can make to our code in 5.2.
To start, perhaps instead of both buttons showing, we would like a dropdown menu. This can be achieved by changing the type = "buttons"
code in the line list(y = 0.8, type = "buttons",
to type = "dropdown"
(which makes sense).
Try making this change now.
You will have noticed that the histogram looks a little different to the one from the Data Science computer lab 2. This is because we have not specified that the histograms should overlay each other. Recall that we can do this via the barmode ="overlay"
command.
Normally, we would have to specify this when creating our original plot. However, by using the pipe operator and the layout
function, we can easily add this in to our new plot. Try inserting this command in your code for our new penguins_plots
object.
Hint: It doesn’t need to be added within the updatemenus
function.
Finally, let’s add an appropriate title and a range slider to our new penguins_plots
.
Hint: Refer back to our previous steps, e.g. 2.3 and 3.1, if you are not quite sure how to proceed.
Well done! There was a lot of content today.
Don’t worry if you weren’t able to finish everything in the one session - there is quite of lot of material to work through in this lab, and it’s not easy.
Hopefully though, you are beginning to feel quite skilled with using plotly
. The techniques and coding skills you are learning should hold you in good stead for the following weeks. Remember, you can always refer back to this material at a later date if you need a quick refresher.
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.
LS0tDQp0aXRsZTogIlNUTTEwMDE6IENvbXB1dGVyIExhYiAzQiINCm91dHB1dDoNCiAgYm9va2Rvd246Omh0bWxfZG9jdW1lbnQyOiANCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZmxvYXQ6IHRydWUNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgdGhlbWU6IHJlYWRhYmxlDQogICAgY29kZV9mb2xkaW5nOiBzaG93DQpiaWJsaW9ncmFwaHk6IFNUTTEwMDFfRFNfQ0xfcmVmZXJlbmNlcy5iaWIgDQpsaW5rLWNpdGF0aW9uczogeWVzDQotLS0NCg0KPHN0eWxlPg0KI1RPQyB7DQogIGJhY2tncm91bmQ6IHVybCgiaHR0cHM6Ly93d3cubGF0cm9iZS5lZHUuYXUvX21lZGlhL2xhLXRyb2JlLWFwaS92NS9pbWcvbG9nby5zdmciKTsNCiAgYmFja2dyb3VuZC1zaXplOiBjb250YWluOw0KICBwYWRkaW5nLXRvcDogODBweCAhaW1wb3J0YW50Ow0KICBiYWNrZ3JvdW5kLXJlcGVhdDogbm8tcmVwZWF0Ow0KfQ0KPC9zdHlsZT4NCg0KIyMjIERhdGEgU2NpZW5jZSBNb2R1bGUgey19DQoNCiMjIyBUb3BpYyAzQjogRGF0YSBWaXN1YWxpc2F0aW9uIElJSSB7LX0NCg0KPGJyPg0KDQpXZWxjb21lIHRvIHRoZSB0aGlyZCBEYXRhIFNjaWVuY2UgY29tcHV0ZXIgbGFiLg0KTGFzdCB3ZWVrLCBpbiB0aGUgW0RhdGEgU2NpZW5jZSBjb21wdXRlciBsYWIgMl0oaHR0cHM6Ly9ycHVicy5jb20vTFRVX1NUTTEwMDEvRFNNQ0wyX1MpLCB3ZSBmYW1pbGlhcmlzZWQgb3Vyc2VsdmVzIHdpdGggdGhlIGBwbG90bHlgIGZ1bmN0aW9uLCBhbmQgbWFkZSBzb21lIGluZm9ybWF0aXZlIGFuZCBpbnRlcmFjdGl2ZSBwbG90cywgdXNpbmcgdGhlIGBwYWxtZXJwZW5ndWluc2AgUiBkYXRhIHNldCBbQHBlbmd1aW5zXS4NCg0KVGhpcyB3ZWVrLCB3ZSB3aWxsIGZvY3VzIG9uIGZ1cnRoZXIgZGV2ZWxvcGluZyBvdXIgc2tpbGxzIHdpdGggYHBsb3RseWAgW0BwbG90bHldLCBhbmQgY292ZXIgYWRkaW5nIGN1c3RvbSBjb250cm9scyBhbmQgYW5pbWF0aW9ucyB0byBvdXIgYHBsb3RseWAgcGxvdHMuIFRoZSBjb2RpbmcgaW4gdGhpcyBsYWIgaXMgYSBsaXR0bGUgbW9yZSBpbnRlbnNlIHRoYW4gaW4gcHJldmlvdXMgd2Vla3MsIGJ1dCB3ZSB3aWxsIHRha2Ugb3VyIHRpbWUsIGFuZCBnbyB0aHJvdWdoIGVhY2ggb2YgdGhlIHN0ZXBzIHNsb3dseS4gDQoNCioqQmVmb3JlIHlvdSBiZWdpbiB0aGlzIGxhYiwgbWFrZSBzdXJlIHlvdSBoYXZlIHJlYWQgb3ZlciBbU2VjdGlvbiA0IG9mIHRoZSBEYXRhIFZpc3VhbGlzYXRpb24gaW4gUiBzdXBwbGVtZW50XShodHRwczovL2Jvb2tkb3duLm9yZy9yZWhrL3N0bTEwMDFfZHNtX3QxX2RhdGFfdmlzdWFsaXNhdGlvbl9pbl9yL3ItY29kaW5nLXRlY2huaXF1ZXMuaHRtbCkgb24gcGlwaW5nLCBhbmQgW1NlY3Rpb24gNSBvZiB0aGUgRGF0YSBWaXN1YWxpc2F0aW9uIGluIFIgc3VwcGxlbWVudF0oaHR0cHM6Ly9ib29rZG93bi5vcmcvcmVoay9zdG0xMDAxX2RzbV90MV9kYXRhX3Zpc3VhbGlzYXRpb25faW5fci9hZHZhbmNlZC1wbG90bHktY29tcHV0ZXItbGFiLTNiLmh0bWwpIG9uIGFkZGluZyBidXR0b25zIHRvIGBwbG90bHlgIGdyYXBocy4qKg0KDQpCeSB0aGUgZW5kIG9mIHRoaXMgbGFiLCB5b3Ugc2hvdWxkIHVuZGVyc3RhbmQgcGlwaW5nLCBiZSBhYmxlIHRvIGNyZWF0ZSBgcGxvdGx5YCBncmFwaHMgd2l0aCBjdXN0b21pemVkIHNsaWRlcnMgYW5kIGRyb3Bkb3duIGJ1dHRvbnMsIGFuZCBoYXZlIGEgZGVjZW50IGlkZWEgb2YgaG93IHRvIGFuaW1hdGUgYHBsb3RseWAgZ3JhcGhzLg0KDQo8YnI+DQoNCiMgUHJlcGFyYXRpb24geyNsb2FkaW5nfQ0KDQpCZWZvcmUgd2UgYmVnaW4gb3VyIHdvcmssIHdlIHdpbGwgbmVlZCB0byBjYXJyeSBvdXQgc29tZSBpbml0aWFsIHByZXBhcmF0aW9ucy4NCg0KIyMNCg0KVG8gYmVnaW4sIHdlIHdpbGwgbmVlZCB0byBsb2FkIGFsbCB0aGUgcmVxdWlzaXRlIHBhY2thZ2VzLg0KQnkgbm93LCB5b3Ugc2hvdWxkIGhhdmUgdGhlIGBwYWxtZXJwZW5ndWluc2AgYW5kIGBwbG90bHlgIHBhY2thZ2VzIGluc3RhbGxlZCBvbiB5b3VyIHN5c3RlbS4NCk9wZW4gdXAgUlN0dWRpbyBhbmQgbG9hZCB0aGVzZSBwYWNrYWdlcyBub3cuDQoNCipJZiBmb3Igc29tZSByZWFzb24geW91IGRvIG5vdCBoYXZlIG9uZSBvciBib3RoIG9mIHRoZXNlIHBhY2thZ2VzIGluc3RhbGxlZCwganVzdCBjbGljayB0aGUgYENvZGVgIGJ1dHRvbiBiZWxvdyBmb3IgdGhlIHJlbGV2YW50IGNvZGUgeW91IHdpbGwgbmVlZCB0byBydW4uKg0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIiwgZXZhbCA9IEYsIGVjaG8gPSBUfQ0KaW5zdGFsbC5wYWNrYWdlcygicGFsbWVycGVuZ3VpbnMiKQ0KaW5zdGFsbC5wYWNrYWdlcygicGxvdGx5IikNCmBgYA0KDQoqSWYgeW91IG5lZWQgYSBxdWljayByZWZyZXNoZXIgb24gaG93IHRvIGxvYWQgcGFja2FnZXMgaW4gUiwganVzdCBjbGljayB0aGUgYENvZGVgIGJ1dHRvbiBiZWxvdy4qDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUiLCBldmFsID0gRiwgZWNobyA9IFR9DQpsaWJyYXJ5KHBhbG1lcnBlbmd1aW5zKQ0KbGlicmFyeShwbG90bHkpDQpgYGANCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBULCBpbmNsdWRlID0gRn0NCmxpYnJhcnkocGFsbWVycGVuZ3VpbnMpDQpsaWJyYXJ5KHBsb3RseSkNCmBgYA0KDQojIFBpcGluZw0KDQoNClJlY2FsbCBmcm9tIFtTZWN0aW9uIDQuMSBvZiB0aGUgRGF0YSBWaXN1YWxpc2F0aW9uIGluIFIgc3VwcGxlbWVudF0oaHR0cHM6Ly9ib29rZG93bi5vcmcvcmVoay9zdG0xMDAxX2RzbV90MV9kYXRhX3Zpc3VhbGlzYXRpb25faW5fci9yLWNvZGluZy10ZWNobmlxdWVzLmh0bWwpICB0aGF0IHRoZSBwaXBlIG9wZXJhdG9yIGNhbiBiZSB1c2VkIHRvIGNoYWluIHRvZ2V0aGVyIGEgc2VxdWVuY2Ugb2Ygb3BlcmF0aW9ucyBpbiBSLCBpbiBhbiBpbnR1aXRpdmUgbWFubmVyIHdoaWNoIGlzIHR5cGljYWxseSBlYXNpZXIgdG8gcmVhZCB0aGFuIGFsdGVybmF0aXZlIG1ldGhvZHMuIFBpcGluZyBjYW4gYmUgdXNlZCB0byBhZGQgYWRkaXRpb25hbCBkZXRhaWxzIHRvIGV4aXN0aW5nIG9iamVjdHMsIHdpdGhvdXQgdGhlIG5lZWQgdG8gZGVmaW5lIG5ldyBvYmplY3RzLg0KDQpMZXQncyB0YWtlIGEgbG9vayBhdCBhIHNpbXBsZSBleGFtcGxlLg0KDQojIyB7I3NpbXBsZXNjYXR0ZXJ9DQoNClJlY2FsbCBpbiBzZWN0aW9uIDIgb2YgdGhlIFtEYXRhIFNjaWVuY2UgY29tcHV0ZXIgbGFiIDJdKGh0dHBzOi8vcnB1YnMuY29tL0xUVV9TVE0xMDAxL0RTTUNMMl9TKSB0aGF0IHdlIGNyZWF0ZWQgYSBzaW1wbGUgc2NhdHRlciBwbG90IHVzaW5nIHRoZSBgYm9keV9tYXNzX2dgIGFuZCBgZmxpcHBlcl9sZW5ndGhfbW1gIHZhcmlhYmxlcyBmcm9tIHRoZSBgcGVuZ3VpbnNgIGRhdGEgc2V0LiBXZSBhbHNvIHVzZWQgZGlmZmVyZW50IGNvbG91cnMgdG8gZGlzdGluZ3Vpc2ggYmV0d2VlbiBtYWxlIGFuZCBmZW1hbGUgcGVuZ3VpbnMuIFRoaXMgY29kZSBpcyByZXByb2R1Y2VkIGJlbG93Og0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IFQsIGVjaG8gPSBUfQ0KcGVuZ3VpbnNfc2NhdHRlciA8LSBwbG90X2x5KGRhdGEgPSBwZW5ndWlucywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgeCA9IH5ib2R5X21hc3NfZywgeSA9IH5mbGlwcGVyX2xlbmd0aF9tbSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSB+c2V4LCBjb2xvcnMgPSAiU2V0MSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZSA9ICJzY2F0dGVyIiwgbW9kZSA9ICJtYXJrZXJzIikNCmBgYA0KDQojIyB7I3NpbXBsZXNjYXR0ZXIyfQ0KDQpXaGVuIHdlIGhvdmVyIG92ZXIgYSBwb2ludCBpbiBvdXIgc2NhdHRlciBwbG90LCB3ZSBzZWUgdGhlIGZsaXBwZXIgbGVuZ3RoLCBib2R5IG1hc3MsIGFuZCBzZXggZGV0YWlscyBmb3IgdGhhdCBwb2ludC4gVGhpcyBpcyBncmVhdCwgYnV0IHdlIGFyZSBtaXNzaW5nIG9uZSBpbXBvcnRhbnQgcGllY2Ugb2YgaW5mb3JtYXRpb24gLSB0aGUgc3BlY2llcyBvZiBwZW5ndWluISBGb3J0dW5hdGVseSwgaXQgaXMgc3RyYWlnaHRmb3J3YXJkIHRvIGFkZCB0aGlzIGluZm9ybWF0aW9uIHRvIHRoZSBob3ZlciB0ZXh0LiBXZSBjYW4gZG8gdGhpcyBieSBpbmNsdWRpbmcgdGhlIGFyZ3VtZW50IGB0ZXh0ID0gfnNwZWNpZXNgIGluIG91ciBjb2RlLCBpbiBhIHNpbWlsYXIgd2F5IHRvIGhvdyB3ZSBoYXZlIHVzZWQgYGNvbG9yID0gfnNleGAgdG8gY29sb3VyIHRoZSBwb2ludHMuDQoNClVwZGF0ZSB5b3VyIGBwZW5ndWluc19zY2F0dGVyYCBwbG90IHdpdGggdGhpcyBhZGRpdGlvbiBub3csIGFuZCBob3ZlciBvdmVyIHNvbWUgcG9pbnRzIHRvIGNoZWNrIHRoYXQgeW91ciBjb2RlIGhhcyB3b3JrZWQgYXMgaW50ZW5kZWQuDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gVCwgaW5jbHVkZSA9IEZ9DQpwZW5ndWluc19zY2F0dGVyIDwtIHBsb3RfbHkoZGF0YSA9IHBlbmd1aW5zLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB4ID0gfmJvZHlfbWFzc19nLCB5ID0gfmZsaXBwZXJfbGVuZ3RoX21tLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IH5zZXgsIGNvbG9ycyA9ICJTZXQxIiwgdGV4dCA9IH5zcGVjaWVzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAic2NhdHRlciIsIG1vZGUgPSAibWFya2VycyIpDQpgYGANCg0KIyMgeyN0aXRsZX0NCg0KU3VwcG9zZSB0aGF0IHdlIHdvdWxkIGxpa2UgdG8gYWRkIGEgdGl0bGUgdG8gdGhlIGBwZW5ndWluc19zY2F0dGVyYCBwbG90IGFib3ZlLiANCkluc3RlYWQgb2YgcmV3cml0aW5nIG91ciBgcGxvdF9seWAgZnVuY3Rpb24gaW4gXEByZWYoc2ltcGxlc2NhdHRlcjIpIGFuZCBhc3NpZ25pbmcgdGhlIG91dHB1dCB0byBhIG5ldyBvYmplY3QgKGUuZy4gYHBlbmd1aW5zX3NjYXR0ZXIyYCksIHdlIGNvdWxkIHVzZSB0aGUgcGlwZSBvcGVyYXRvciB0byBhZGQgdGhpcyBpbmZvcm1hdGlvbiB0byBvdXIgYHBlbmd1aW5zX3NjYXR0ZXJgIHBsb3QuDQoNClRoZSBjb2RlIGJlbG93IGRvZXMganVzdCB0aGlzOg0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IFQsIGVjaG8gPSBULCB3YXJuaW5nID0gRn0NCnBlbmd1aW5zX3NjYXR0ZXIgJT4lIGxheW91dCh0aXRsZSA9ICJTY2F0dGVyIFBsb3Qgb2YgUGVuZ3VpbiBEYXRhIikNCmBgYA0KDQojIyB7I2xlZ2VuZHRpdGxlfQ0KDQpXZSBjYW4gYWxzbyBhZGQgYSB0aXRsZSB0byBvdXIgbGVnZW5kLiBUaGlzIGNhbiBvZnRlbiBoZWxwIHRvIG1ha2Ugb3VyIGdyYXBocyBtb3JlIGluZm9ybWF0aXZlLg0KVHJ5IHJ1bm5pbmcgdGhlIGNvZGUgYmVsb3csIGFuZCBjaGVjayB0aGUgcmVzdWx0Og0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IEYsIGVjaG8gPSBULCB3YXJuaW5nID0gRn0NCnBlbmd1aW5zX3NjYXR0ZXIgJT4lIGxheW91dCh0aXRsZSA9ICJTY2F0dGVyIFBsb3Qgb2YgUGVuZ3VpbiBEYXRhIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kPWxpc3QodGl0bGU9bGlzdCh0ZXh0PSdTZXgnKSkpDQpgYGANCg0KIyMNCg0KSW4gdGhlIFxAcmVmKGxlZ2VuZHRpdGxlKSBjb2RlLCB3ZSB3ZXJlIGFibGUgdG8gYWRkIGRldGFpbHMgdG8gbXVsdGlwbGUgY29tcG9uZW50cyBvZiBvdXIgcGxvdCwgdmlhIHRoZSBgbGF5b3V0YCBmdW5jdGlvbi4gR2VuZXJhbGx5LCB3aGVuIHdlIG1ha2UgY2hhbmdlcyB0byBgcGxvdGx5YCBwbG90cyB2aWEgcGlwaW5nLCB3ZSBhcmUgbWFraW5nIGNoYW5nZXMgdG8gdGhlIGxheW91dCwgcmF0aGVyIHRoYW4gdGhlIGNvcmUgZGF0YSBiZWluZyB2aXN1YWxpc2VkLg0KDQpXaXRoaW4gdGhlIGBsYXlvdXRgIGZ1bmN0aW9uLCB3ZSBoYXZlIHVzZWQgdGhlIGFyZ3VtZW50IGB0aXRsZWAgKHRoZSBmdW5jdGlvbiBvZiB3aGljaCBpcyB0bywgcmF0aGVyIGFwcHJvcHJpYXRlbHksIGNoYW5nZSB0aGUgdGl0bGUpLiBUaGlzIGlzIG9uZSBvZiBtYW55IHBvc3NpYmxlIGFyZ3VtZW50cyAtIHNvbWUgeW91IHdpbGwgbGVhcm4gYXMgd2UgZGV2ZWxvcCBvdXIgdW5kZXJzdGFuZGluZyBvZiBgcGxvdGx5YCwgYW5kIHNvbWUgeW91IG1heSBuZXZlciB1c2UsIGFzIHRoZXkgYXJlIHF1aXRlIGNvbnRleHQgc3BlY2lmaWMuIFR5cGljYWxseSB0aG91Z2gsIHRoZSBuYW1lcyBvZiB0aGUgYXJndW1lbnRzIGFyZSBjbGVhciBhbmQgZWFzeSB0byByZW1lbWJlciAtIGZvciBpbnN0YW5jZSwgYGxlZ2VuZGAgYWxsb3dzIHVzIHRvIGNoYW5nZSBkZXRhaWxzIGluIHRoZSBsZWdlbmQuDQoNCiMjDQoNClRvIGNvbmNsdWRlIHRoaXMgZXhhbXBsZSwgc3VwcG9zZSB0aGF0IHdlIHdvdWxkIGxpa2UgdG8gcmVuYW1lIG91ciBwbG90J3MgeC1heGlzIGFuZCB5LWF4aXMuIFRoZSBkZWZhdWx0IG5hbWVzIGFyZSBvaywgYnV0IHBlcmhhcHMgd2Ugd291bGQgbGlrZSBzb21ldGhpbmcgYSBsaXR0bGUgZGlmZmVyZW50LiBUYWtlIGEgbG9vayBhdCB0aGUgY29kZSBiZWxvdywgYW5kIHRyeSBmaWxsaW5nIGluIHRoZSBtaXNzaW5nIGRldGFpbHMgKGRlbm90ZWQgYnkgdGhlIC4uLnMpIGZvciB0aGUgeWF4aXM6DQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gRiwgZWNobyA9IFQsIHdhcm5pbmcgPSBGfQ0KcGVuZ3VpbnNfc2NhdHRlciAlPiUgbGF5b3V0KHhheGlzID0gbGlzdCh0aXRsZSA9ICJQZW5ndWluIEJvZHkgTWFzcyAoZ3JhbXMpIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgLi4uKQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICkNCmBgYA0KDQpNYWtlIHN1cmUgdG8gcnVuIHlvdXIgY29kZSB0byB2ZXJpZnkgaXQgaXMgd29ya2luZyBhcyBpbnRlbmRlZC4NCg0KKkhpbnQ6IElmIHlvdSB3b3VsZCBsaWtlIHRvIGNoZWNrIHlvdXIgd29yaywgb3IgYXJlIG5vdCBxdWl0ZSBzdXJlIGlmIHlvdSBhcmUgb24gdGhlIHJpZ2h0IHRyYWNrLCBqdXN0IGNsaWNrIHRoZSBgQ29kZWAgYnV0dG9uIGJlbG93LioNCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSIsIGV2YWwgPSBGLCBlY2hvID0gVCwgd2FybmluZyA9IEZ9DQojIEFuIGV4YW1wbGUgZm9yIHRoZSB5YXhpcyBjb2RlIGlzIHNob3duIGJlbG93DQpwZW5ndWluc19zY2F0dGVyICU+JSBsYXlvdXQoeGF4aXMgPSBsaXN0KHRpdGxlID0gIlBlbmd1aW4gQm9keSBNYXNzIChncmFtcykiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAiUGVuZ3VpbiBGbGlwcGVyIExlbmd0aCAobW0pIikNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICApDQpgYGANCg0KIyMNCg0KTm90aWNlIHRoYXQgd2UgaGF2ZSBpbmNsdWRlZCB0aGUgYGxpc3RgIGZ1bmN0aW9uIHdpdGhpbiBvdXIgYGxheW91dGAgZnVuY3Rpb24gY29kaW5nLg0KVGhlIGB4YXhpc2AgYW5kIGB5YXhpc2AgYXJndW1lbnRzIGNhbiBib3RoIHRha2Ugc2V2ZXJhbCBzZXR0aW5ncyAtIGZvciBleGFtcGxlLCB3ZSBjb3VsZCBjaGFuZ2UgdGhlIHgtYXhpcyB0aXRsZSwgYW5kIGZvbnQgc2l6ZS4gVGhpcyBpcyB0eXBpY2FsbHkgdGhlIGNhc2UgZm9yIGBsYXlvdXRgIGFyZ3VtZW50cyAodGhlIHRpdGxlIGluIFxAcmVmKHRpdGxlKSB3YXMgYW4gZXhjZXB0aW9uKS4gDQoNClRoZXJlZm9yZSwgcGxlYXNlIGtlZXAgaW4gbWluZCB0aGF0IGdlbmVyYWxseSBzcGVha2luZywgaW4gdGhlIGNvbnRleHQgb2Ygb3VyIGBwbG90bHlgIGdyYXBocywgd2hlbiBkZWFsaW5nIHdpdGggdGhlIGBsYXlvdXRgIGZ1bmN0aW9uIHdlIG5lZWQgdG8gdXNlIHRoZSBgbGlzdGAgZnVuY3Rpb24gYmVmb3JlIHNwZWNpZnlpbmcgb3VyIGRlc2lyZWQgY2hhbmdlcyB0byBgbGF5b3V0YCBhcmd1bWVudHMuDQoNCiMjDQoNCkFzIGEgZmluYWwgbm90ZSwgaXQncyB3b3J0aCBwb2ludGluZyBvdXQgdGhhdCBvdXIgbWFpbiB0aXRsZSBmcm9tIFxAcmVmKHRpdGxlKSBoYXMgZGlzYXBwZWFyZWQgaW4gb3VyIG5ldyBwbG90LiBUaGlzIGlzIGJlY2F1c2Ugd2UgZGlkIG5vdCBhc3NpZ24gb3VyIGVuaGFuY2VkIHBsb3QgdG8gYSBuZXcgb2JqZWN0LiBXaGVuIHdlIHVzZSBwaXBpbmcsIHdlIGFyZSBub3QgbW9kaWZ5aW5nIHRoZSBvcmlnaW5hbCBvYmplY3QsIGJ1dCByYXRoZXIgYXJlIGNhcnJ5aW5nIG91dCBvcGVyYXRpb25zIG9uL3dpdGggaXQuIFRoZXJlZm9yZSBhbnkgY2hhbmdlcyB3ZSBpbXBsZW1lbnQgYXJlIG5vdCBzYXZlZCB0byB0aGUgb3JpZ2luYWwgb2JqZWN0Lg0KDQojIEFkZGluZyBSYW5nZSBTbGlkZXJzIHRvIFBsb3RseSBwbG90cyB7I3NsaWRlcn0gDQoNCk5vdyB0aGF0IHdlIGhhdmUgZGlzY3Vzc2VkIHRoZSBwaXBlIG9wZXJhdG9yIGFuZCB0aGUgYGxheW91dGAgZnVuY3Rpb24sIGxldCdzIGNvbnNpZGVyIGFkZGluZyBzb21lIGZhbmNpZXIgZWxlbWVudHMgdG8gb3VyIGBwbG90bHlgIGdyYXBocy4gRmlyc3QsIGxldCdzIHRha2UgYSBsb29rIGF0IGFkZGluZyBhICoqcmFuZ2Ugc2xpZGVyKiogdG8gb3VyIHBsb3QuDQoNCiMjIHsjc2xpZGVyc3RhcnR9DQoNClN1cHBvc2UgdGhhdCB3ZSB3b3VsZCBsaWtlIHRvIGFkZCBhIHJhbmdlIHNsaWRlciB0byBvdXIgeC1heGlzLiBBIHJhbmdlIHNsaWRlciBjYW4gYmUgdXNlZCB0byBkeW5hbWljYWxseSBzZWxlY3QgYSBzdWJzZWN0aW9uIG9mIG91ciBwbG90LCBpbiBhIHNpbWlsYXIsIGJ1dCBtb3JlIGNvbnRyb2xsZWQgd2F5IHRvIGxlZnQtY2xpY2tpbmcgYW5kIGRyYWdnaW5nIGEgYm94IG92ZXIgb3VyIHBsb3QgdG8gem9vbSBpbiBvbiBhIHNlY3Rpb24uDQoNCldlIGNhbiBhZGQgYSByYW5nZSBzbGlkZXIgdG8gdGhlIHgtYXhpcyBvZiBvdXIgcGxvdCB1c2luZyB0aGUgZnVuY3Rpb24gYHJhbmdlc2xpZGVyKClgLg0KDQpVc2UgdGhlIHBpcGUgb3BlcmF0b3IgdG8gYWRkIGEgcmFuZ2Ugc2xpZGVyIHRvIG91ciBzY2F0dGVyIHBsb3QgZnJvbSBcQHJlZihzaW1wbGVzY2F0dGVyMikuDQoNCiMjDQpJZiB5b3VyIGNvZGUgaGFzIHdvcmtlZCwgeW91IHNob3VsZCBvYnRhaW4gdGhlIHBsb3Qgc2hvd24gYmVsb3cgLSB0cnkgbGVmdC1jbGlja2luZyBhbmQgZHJhZ2dpbmcgdGhlIGJhcnMgb24gdGhlIHNsaWRlciBlbmRwb2ludHMuDQoNCipIaW50OiBZb3UgY2FuIGFsd2F5cyBjaGVjayB0aGUgYENvZGVgIGJveCBiZWxvdyBpZiB5b3VyIGNvZGUgaXMgbm90IHdvcmtpbmcuKg0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIiwgZXZhbCA9IFQsIGVjaG8gPSBULCB3YXJuaW5nID0gRn0NCnBlbmd1aW5zX3NjYXR0ZXIgJT4lIHJhbmdlc2xpZGVyKCkNCiMgTm90IHRvbyBzY2FyeSBzbyBmYXIhDQpgYGANCg0KIyMNCg0KSW5pdGlhbGx5LCBhZGRpbmcgYSByYW5nZSBzbGlkZXIgdG8geW91ciBwbG90IG1pZ2h0IGhhdmUgc2VlbWVkIHZlcnkgZGlmZmljdWx0Lg0KSG93ZXZlciwgYXMgeW91IGNhbiBzZWUsIGRlc3BpdGUgYmVpbmcgYW4gaW1wcmVzc2l2ZSBhZGRpdGlvbiBpdCdzIGFjdHVhbGx5IHZlcnkgc3RyYWlnaHRmb3J3YXJkIHRvIGltcGxlbWVudC4NCg0KT2YgY291cnNlLCB0aGVyZSBhcmUgdmFyaW91cyBhZGRpdGlvbmFsIGFkanVzdG1lbnRzIHRoYXQgd2UgY291bGQgbWFrZSB0byBvdXIgcmFuZ2Ugc2xpZGVyIChhbmQgd2Ugd2lsbCBjb21lIGJhY2sgdG8gcmFuZ2Ugc2xpZGVycyBsYXRlciBvbiBpbiB0aGUgc2VtZXN0ZXIpLCBidXQgZm9yIG5vdywgdGhpcyBvbmUgbGluZSBvZiBjb2RlIGlzIHN1ZmZpY2llbnQgZm9yIG91ciBwdXJwb3Nlcy4NCg0KDQojIENyZWF0aW5nIGFuaW1hdGVkIFBsb3RseSBwbG90cyB7I2FuaW1hdGlvbnN9DQoNCkFub3RoZXIgKGV2ZW4gbW9yZSkgaW1wcmVzc2l2ZSBhZGRpdGlvbiB3ZSBjYW4gbWFrZSB0byBvdXIgcGxvdCBpcyB0byB0dXJuIGl0IGludG8gYW4gYW5pbWF0aW9uLg0KDQpZb3UgbWlnaHQgcmVjYWxsIHRoYXQgb25lIG9mIHRoZSB2YXJpYWJsZXMgaW4gdGhlIGBwZW5ndWluc2AgZGF0YSBzZXQgd2hpY2ggd2UgaGF2ZW4ndCByZWFsbHkgY29uc2lkZXJlZCBzbyBmYXIgaXMgdGhlIGB5ZWFyYCB2YXJpYWJsZSAtIG5hbWVseSwgd2UgaGF2ZSBwZW5ndWluIGRhdGEgcmVjb3JkZWQgZm9yIHRoZSB5ZWFycyAyMDA3LCAyMDA4IGFuZCAyMDA5Lg0KDQpTdXBwb3NlIHdlIHdvdWxkIGxpa2UgdG8gc2VlIGhvdyB0aGUgYGJvZHlfbWFzc19nYCBhbmQgYGJpbGxfbGVuZ3RoX21tYCB2YWx1ZXMgb2YgdGhlIG1hbGUgYW5kIGZlbWFsZSBwZW5ndWlucyBjaGFuZ2VzIG92ZXIgdGhlIHllYXJzLiBXZSBhbHJlYWR5IGhhdmUgdGhpcyBpbmZvcm1hdGlvbiBzdG9yZWQgYXdheSBpbiB0aGUgYHBlbmd1aW5zYCBkYXRhIHNldCwgYnV0IHdlIGhhdmVuJ3QgdmlzdWFsaXNlZCBpdCB5ZXQuDQoNCkNvdWxkIHdlIHNvbWVob3cgbW9kaWZ5IG91ciBzY2F0dGVyIHBsb3QgdG8gc2hvdyBkYXRhIGZvciBlYWNoIHllYXIsIGFuZCBkeW5hbWljYWxseSBzd2l0Y2ggYmV0d2VlbiB5ZWFycyBvbiBjb21tYW5kPyBJcyBzdWNoIGEgdGhpbmcgZXZlbiBwb3NzaWJsZT8gV2h5LCB3aXRoIGBwbG90bHlgLCB5ZXMgaXQgaXMhDQoNCiMjDQoNCkFkZGluZyBhbmltYXRpb25zIHRvIGEgYHBsb3RseWAgcGxvdCBpcyBzdXJwcmlzaW5nbHkgZWFzeSwgYnV0IHdlIG5lZWQgdG8gZW5zdXJlIHRoYXQgb3VyIGRhdGEgYW5kIGNvZGUgaXMgc2V0IHVwIHByb3Blcmx5LiBGb3J0dW5hdGVseSwgaW4gdGhpcyBpbnN0YW5jZSB0aGUgYHBlbmd1aW5zYCBkYXRhIHNldCB3aGljaCB3ZSBhcmUgdXNpbmcgYWxyZWFkeSBjb250YWlucyB0aGUgaW5mb3JtYXRpb24gd2Ugd291bGQgbGlrZSB0byB1c2UgZm9yIG91ciBhbmltYXRpb24uIA0KDQpUaGUgYXJndW1lbnQgd2Ugd2lsbCB1c2UgdG8gdHVybiBvdXIgc2NhdHRlciBwbG90IGludG8gYW4gYW5pbWF0ZWQgcGxvdCBpcyBgZnJhbWUgPSBgLiBUaGF0J3MgaXQuDQpXZSBuZWVkIHRvIGluY2x1ZGUgdGhpcyBpbnNpZGUgb3VyIGBwbG90bHkoKWAgZnVuY3Rpb24sIGluIGEgc2ltaWxhciBmYXNoaW9uIHRvIGhvdyB3ZSB1c2UgYHg9YCBhbmQgYHk9YCB3aGVuIGFzc2lnbmluZyB0aGUgZGF0YSBmb3Igb3VyIGB4YCBhbmQgYHlgIHZhcmlhYmxlcy4NCg0KVHJ5IHVzaW5nIHRoZSBgZnJhbWUgPSBgIGFyZ3VtZW50IHRvIGFkZCB0aGUgYHllYXJgIHZhcmlhYmxlIGludG8gb3VyIHNjYXR0ZXIgcGxvdCBmcm9tIFxAcmVmKHNpbXBsZXNjYXR0ZXIyKS4gWW91IG1heSB3YW50IHRvIGFzc2lnbiB0aGUgbmV3IHBsb3QgdG8gYSBuZXcgb2JqZWN0IC0gZS5nLiBgcGVuZ3VpbnNfc2NhdHRlcl9hbmltYC4NCg0KIyMNCg0KWW91ciBlbmQgcmVzdWx0IHNob3VsZCBsb29rIGxpa2UgdGhlIHBsb3QgYmVsb3c6DQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gVCwgZWNobyA9IEYsIHdhcm5pbmcgPSBGfQ0KcGVuZ3VpbnNfc2NhdHRlcl9hbmltIDwtIHBsb3RfbHkoZGF0YSA9IHBlbmd1aW5zLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB4ID0gfmJvZHlfbWFzc19nLCB5ID0gfmZsaXBwZXJfbGVuZ3RoX21tLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IH5zZXgsIGNvbG9ycyA9ICJTZXQxIiwgdGV4dCA9IH5zcGVjaWVzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZyYW1lID0gfnllYXIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZSA9ICJzY2F0dGVyIiwgbW9kZSA9ICJtYXJrZXJzIikNCnBlbmd1aW5zX3NjYXR0ZXJfYW5pbQ0KYGBgDQoNCklmIHlvdSBoYXZlbid0IGFscmVhZHksIHRyeSBjbGlja2luZyBvbiB0aGUgYFBsYXlgIGJ1dHRvbiwgdG8gd2F0Y2ggdGhlIGFuaW1hdGlvbiB1bmZvbGQuDQpJZiBpdCdzIGEgbGl0dGxlIGZhc3QsIHlvdSBjYW4gYWxzbyBjbGljayBhbmQgZHJhZyB0aGUgY2lyY2xlIGluIHRoZSBzbGlkZXIgdG8gY2hhbmdlIGJldHdlZW4geWVhcnMuDQoNCiMjDQoNCk5leHQsIGluc3RlYWQgb2YgdXNpbmcgdGhlIGB5ZWFyYCB2YXJpYWJsZSwgY3JlYXRlIGEgc2NhdHRlciBwbG90IGFuaW1hdGlvbiB0aGF0IGN5Y2xlcyB0aHJvdWdoIHRoZSBkaWZmZXJlbnQgYHNwZWNpZXNgIG9mIHBlbmd1aW4gaW4gdGhlIGBwZW5ndWluc2AgZGF0YSBzZXQuIENoYW5nZSB5b3VyIGhvdmVyIHRleHQgZnJvbSBzaG93aW5nIHRoZSBzcGVjaWVzIG9mIHBlbmd1aW4sIHRvIHNob3dpbmcgdGhlIHllYXIgdGhlIGRhdGEgd2FzIHJlY29yZGVkLg0KDQpXaGF0IGRvIHlvdSBub3RpY2UgYWJvdXQgdGhlIGRpZmZlcmVudCBzcGVjaWVzPw0KDQojIEFkZGluZyBidXR0b25zIHRvIFBsb3RseSBwbG90cyB7I2J1dHRvbn0NCg0KU28gZmFyLCBob3BlZnVsbHkgdGhlIGNvZGluZyBpbiB0aGlzIGxhYiBoYXMgbm90IGJlZW4gdG9vIGludGVuc2UuIFRoYXQncyBhYm91dCB0byBjaGFuZ2UuDQoNCkluIHRoaXMgZmluYWwgc2VjdGlvbiwgd2UnbGwgbG9vayBhdCBob3cgdG8gYWRkIGJ1dHRvbnMgdG8gb3VyIGBwbG90bHlgIHBsb3QsIHdoaWNoICh3aGVuIGNsaWNrZWQpIHdpbGwgYWxsb3cgdXMgdG8gc2hpZnQgYmV0d2VlbiBkaWZmZXJlbnQgcHJlc2VudGF0aW9ucyBvZiBvdXIgZGF0YS4gU3BlY2lmaWNhbGx5LCB3ZSdsbCBhZGQgYnV0dG9ucyB0aGF0IGFsbG93IHVzIHRvIHNoaWZ0IGJldHdlZW4gdmlld2luZyBvdXIgYHBlbmd1aW5zYCBkYXRhIGFzIGEgc2NhdHRlciBwbG90LCBhbmQgYXMgYSBoaXN0b2dyYW0uIA0KDQoqKlBsZWFzZSBub3RlIC0gYmVmb3JlIHlvdSBiZWdpbiB0aGlzIHNlY3Rpb24sIG1ha2Ugc3VyZSB5b3UgaGF2ZSByZWFkIHRocm91Z2ggW1NlY3Rpb24gNSBvZiB0aGUgRGF0YSBWaXN1YWxpc2F0aW9uIGluIFIgc3VwcGxlbWVudF0oaHR0cHM6Ly9ib29rZG93bi5vcmcvcmVoay9zdG0xMDAxX2RzbV90MV9kYXRhX3Zpc3VhbGlzYXRpb25faW5fci9hZHZhbmNlZC1wbG90bHktY29tcHV0ZXItbGFiLTNiLmh0bWwpLiBJdCBtYXkgYWxzbyBiZSBoZWxwZnVsIHRvIGhhdmUgdGhpcyBvcGVuIGluIGEgc2VwYXJhdGUgdGFiLCBzbyB0aGF0IHlvdSBjYW4gcmVmZXIgdG8gaXQgYXMgeW91IHdvcmsgdGhyb3VnaCB0aGlzIHNlY3Rpb24uKioNCg0KIyMNCg0KU29tZXRpbWVzLCBhZGRpbmcgYnV0dG9ucyB0byBgcGxvdGx5YCBwbG90cyB0aGF0IGluY2x1ZGUgYWRkaXRpb25hbCBob3ZlciB0ZXh0IGNhbiBsZWFkIHRvIGRpc3BsYXkgZXJyb3JzLiBUaGVyZWZvcmUsIGJlZm9yZSBjb250aW51aW5nLCBwbGVhc2UgcmUtcnVuIHRoZSBSIGNvZGUgeW91IHVzZWQgaW4gXEByZWYoc2ltcGxlc2NhdHRlcikgKHJlcHJvZHVjZWQgaW4gdGhlIGNvZGUgY2h1bmsgYmVsb3cgZm9yIHlvdXIgY29udmVuaWVuY2UpOg0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIiwgZXZhbCA9IFQsIGVjaG8gPSBUfQ0KcGVuZ3VpbnNfc2NhdHRlciA8LSBwbG90X2x5KGRhdGEgPSBwZW5ndWlucywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgeCA9IH5ib2R5X21hc3NfZywgeSA9IH5mbGlwcGVyX2xlbmd0aF9tbSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSB+c2V4LCBjb2xvcnMgPSAiU2V0MSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZSA9ICJzY2F0dGVyIiwgbW9kZSA9ICJtYXJrZXJzIikNCmBgYA0KDQojIyB7I2J1dHRvbnNwbG90fQ0KDQpUaGUgY29kZSBjaHVuayBiZWxvdyBjb250YWlucyBhbGwgdGhlIGNvZGUgd2UgbmVlZCB0byB0YWtlIG91ciBgcGVuZ3VpbnNfc2NhdHRlcmAgc2NhdHRlciBwbG90LCBhbmQgYWRkIGJ1dHRvbnMgKHVzaW5nICB0aGUgYHVwZGF0ZW1lbnVzYCBhbmQgYGJ1dHRvbnNgIGZ1bmN0aW9ucykgdG8gdGhlIGdyYXBoIHNvIHRoYXQgd2UgY2FuIHN3aXRjaCBiZXR3ZWVuIHZpZXdpbmcgdGhlIGRhdGEgYXMgYSBzY2F0dGVyIHBsb3QgYW5kIGFzIGEgaGlzdG9ncmFtLg0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IFQsIGVjaG8gPSBULCB3YXJuaW5nID0gRn0NCnBlbmd1aW5zX3Bsb3RzIDwtIHBlbmd1aW5zX3NjYXR0ZXIgJT4lIGxheW91dCgNCg0KICB1cGRhdGVtZW51cyA9IGxpc3QoDQogICAgbGlzdCh5ID0gMC44LCB0eXBlID0gImJ1dHRvbnMiLCANCiAgICAgICAgIGJ1dHRvbnMgPSBsaXN0KA0KICAgICAgICAgICANCiAgICAgICAgICBsaXN0KG1ldGhvZCA9ICJyZXN0eWxlIiwNCiAgICAgICAgICAgIGFyZ3MgPSBsaXN0KA0KICAgICAgICAgICAgICBsaXN0KHR5cGUgPSBsaXN0KCJzY2F0dGVyIiksIG1vZGUgPSBsaXN0KCJtYXJrZXJzIikpKSwNCiAgICAgICAgICAgICAgbGFiZWwgPSAiU2NhdHRlciBQbG90IiksDQogICAgICANCiAgICAgICAgICBsaXN0KG1ldGhvZCA9ICJyZXN0eWxlIiwNCiAgICAgICAgICAgIGFyZ3MgPSBsaXN0KA0KICAgICAgICAgICAgICBsaXN0KHR5cGUgPSBsaXN0KCJoaXN0b2dyYW0iKSkpLA0KICAgICAgICAgICAgICBsYWJlbCA9ICJIaXN0b2dyYW0iKSkNCiAgICApKSkNCmBgYA0KDQoqTm90ZSB0aGF0IHRoZSBzcGFjaW5nIGluIHRoZSBjb2RlIGlzIG5vdCBzdHJpY3RseSBuZWNlc3NhcnksIGJ1dCBoYXMgYmVlbiBjaG9zZW4gd2l0aCB0aGUgYWltIG9mIG1ha2luZyB0aGUgZGlmZmVyZW50IGFyZ3VtZW50cyBjbGVhcmVyLioNCg0KUnVuIHRoaXMgY29kZSBub3csIGFuZCB0aGVuIGNhbGwgb3VyIG5ldyBgcGVuZ3Vpbl9wbG90c2Agb2JqZWN0LCB0byBzZWUgdGhlIHJlc3VsdHMuIFRyeSBjbGlja2luZyB0aGUgdHdvIGJ1dHRvbnMgb24gdGhlIHktYXhpcywgdG8gc3dpdGNoIGJldHdlZW4gdGhlIHNjYXR0ZXIgcGxvdCBhbmQgaGlzdG9ncmFtIHZpZXdzIG9mIHRoZSBkYXRhLg0KDQojIw0KDQpMZXQncyB0YWtlIGEgc3RlcCBiYWNrLCBhbmQgY29uc2lkZXIgc29tZSBzbWFsbGVyIG1vZGlmaWNhdGlvbnMgd2UgY2FuIG1ha2UgdG8gb3VyIGNvZGUgaW4gXEByZWYoYnV0dG9uc3Bsb3QpLg0KDQpUbyBzdGFydCwgcGVyaGFwcyBpbnN0ZWFkIG9mIGJvdGggYnV0dG9ucyBzaG93aW5nLCB3ZSB3b3VsZCBsaWtlIGEgZHJvcGRvd24gbWVudS4gVGhpcyBjYW4gYmUgYWNoaWV2ZWQgYnkgY2hhbmdpbmcgdGhlIGB0eXBlID0gImJ1dHRvbnMiYCBjb2RlIGluIHRoZSBsaW5lIGAgbGlzdCh5ID0gMC44LCB0eXBlID0gImJ1dHRvbnMiLCBgIHRvIGB0eXBlID0gImRyb3Bkb3duImAgKHdoaWNoIG1ha2VzIHNlbnNlKS4gDQoNClRyeSBtYWtpbmcgdGhpcyBjaGFuZ2Ugbm93Lg0KDQojIw0KDQpZb3Ugd2lsbCBoYXZlIG5vdGljZWQgdGhhdCB0aGUgaGlzdG9ncmFtIGxvb2tzIGEgbGl0dGxlIGRpZmZlcmVudCB0byB0aGUgb25lIGZyb20gdGhlIFtEYXRhIFNjaWVuY2UgY29tcHV0ZXIgbGFiIDJdKGh0dHBzOi8vcnB1YnMuY29tL0xUVV9TVE0xMDAxL0RTTUNMMl9TKS4gVGhpcyBpcyBiZWNhdXNlIHdlIGhhdmUgbm90IHNwZWNpZmllZCB0aGF0IHRoZSBoaXN0b2dyYW1zIHNob3VsZCBvdmVybGF5IGVhY2ggb3RoZXIuIFJlY2FsbCB0aGF0IHdlIGNhbiBkbyB0aGlzIHZpYSB0aGUgYGJhcm1vZGUgPSJvdmVybGF5ImAgY29tbWFuZC4NCg0KTm9ybWFsbHksIHdlIHdvdWxkIGhhdmUgdG8gc3BlY2lmeSB0aGlzIHdoZW4gY3JlYXRpbmcgb3VyIG9yaWdpbmFsIHBsb3QuIEhvd2V2ZXIsIGJ5IHVzaW5nIHRoZSBwaXBlIG9wZXJhdG9yIGFuZCB0aGUgYGxheW91dGAgZnVuY3Rpb24sIHdlIGNhbiBlYXNpbHkgYWRkIHRoaXMgaW4gdG8gb3VyIG5ldyBwbG90LiBUcnkgaW5zZXJ0aW5nIHRoaXMgY29tbWFuZCBpbiB5b3VyIGNvZGUgZm9yIG91ciBuZXcgYHBlbmd1aW5zX3Bsb3RzYCBvYmplY3QuDQoNCipIaW50OiBJdCBkb2Vzbid0IG5lZWQgdG8gYmUgYWRkZWQgd2l0aGluIHRoZSBgdXBkYXRlbWVudXNgIGZ1bmN0aW9uLioNCg0KIyMNCg0KRmluYWxseSwgbGV0J3MgYWRkIGFuIGFwcHJvcHJpYXRlIHRpdGxlIGFuZCBhIHJhbmdlIHNsaWRlciB0byBvdXIgbmV3IGBwZW5ndWluc19wbG90c2AuIA0KDQoqSGludDogUmVmZXIgYmFjayB0byBvdXIgcHJldmlvdXMgc3RlcHMsIGUuZy4gXEByZWYodGl0bGUpIGFuZCBcQHJlZihzbGlkZXJzdGFydCksIGlmIHlvdSBhcmUgbm90IHF1aXRlIHN1cmUgaG93IHRvIHByb2NlZWQuKg0KDQo8YnI+DQoNCiMjIyMgV2VsbCBkb25lISBUaGVyZSB3YXMgYSBsb3Qgb2YgY29udGVudCB0b2RheS4gIyMjIyB7LX0NCg0KRG9uJ3Qgd29ycnkgaWYgeW91IHdlcmVuJ3QgYWJsZSB0byBmaW5pc2ggZXZlcnl0aGluZyBpbiB0aGUgb25lIHNlc3Npb24gLSB0aGVyZSBpcyBxdWl0ZSBvZiBsb3Qgb2YgbWF0ZXJpYWwgdG8gd29yayB0aHJvdWdoIGluIHRoaXMgbGFiLCBhbmQgaXQncyBub3QgZWFzeS4NCg0KSG9wZWZ1bGx5IHRob3VnaCwgeW91IGFyZSBiZWdpbm5pbmcgdG8gZmVlbCBxdWl0ZSBza2lsbGVkIHdpdGggdXNpbmcgYHBsb3RseWAuIFRoZSB0ZWNobmlxdWVzIGFuZCBjb2Rpbmcgc2tpbGxzIHlvdSBhcmUgbGVhcm5pbmcgc2hvdWxkIGhvbGQgeW91IGluIGdvb2Qgc3RlYWQgZm9yIHRoZSBmb2xsb3dpbmcgd2Vla3MuIFJlbWVtYmVyLCB5b3UgY2FuIGFsd2F5cyByZWZlciBiYWNrIHRvIHRoaXMgbWF0ZXJpYWwgYXQgYSBsYXRlciBkYXRlIGlmIHlvdSBuZWVkIGEgcXVpY2sgcmVmcmVzaGVyLg0KDQpCZWZvcmUgeW91IGZpbmlzaCB1cCwgbWFrZSBzdXJlIHRvIHNhdmUgeW91ciBzY3JpcHQgZmlsZSBzb21ld2hlcmUgc2FmZSAtIGl0IG1pZ2h0IGNvbWUgaW4gaGFuZHkgbGF0ZXIgb24uIA0KPGJyPg0KDQojIFJlZmVyZW5jZXMgey0gI1JlZn0NCjxkaXYgaWQ9InJlZnMiPjwvZGl2Pg0KDQo8YnI+DQoNCjxmb250IGNvbG9yID0gImdyZXkiPg0KVGhlc2Ugbm90ZXMgaGF2ZSBiZWVuIHByZXBhcmVkIGJ5IFJ1cGVydCBLdXZla2UuIFRoZSBjb3B5cmlnaHQgZm9yIHRoZSBtYXRlcmlhbCBpbiB0aGVzZSBub3RlcyByZXNpZGVzIHdpdGggdGhlIGF1dGhvciBuYW1lZCBhYm92ZSwgd2l0aCB0aGUgRGVwYXJ0bWVudCBvZiBNYXRoZW1hdGljYWwgYW5kIFBoeXNpY2FsIFNjaWVuY2VzIGFuZCB3aXRoIExhIFRyb2JlIFVuaXZlcnNpdHkuIENvcHlyaWdodCBpbiB0aGlzIHdvcmsgaXMgdmVzdGVkIGluIExhIFRyb2JlIFVuaXZlcnNpdHkgaW5jbHVkaW5nIGFsbCBMYSBUcm9iZSBVbml2ZXJzaXR5IGJyYW5kaW5nIGFuZCBuYW1pbmcuIFVubGVzcyBvdGhlcndpc2Ugc3RhdGVkLCBtYXRlcmlhbCB3aXRoaW4gdGhpcyB3b3JrIGlzIGxpY2Vuc2VkIHVuZGVyIGEgQ3JlYXRpdmUgQ29tbW9ucyBBdHRyaWJ1dGlvbi1Ob24gQ29tbWVyY2lhbC1Ob24gRGVyaXZhdGl2ZXMgTGljZW5zZSANCjxhIGhyZWYgPSAiaHR0cHM6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL2xpY2Vuc2VzL2J5LW5jLW5kLzQuMC9DQyIgdGFyZ2V0PSJfYmxhbmsiPiBCWS1OQy1ORC4gPC9hPg0KPC9mb250Pg==