Data Science Stream
Topic 4B: Data Visualisation III
Welcome to the fourth computer lab for the Data Science stream of STM1001.
In the second and third Data Science stream computer labs we familiarised ourselves with the plotly
function, and made some informative and interactive plots of data from the palmerpenguins
package (Horst, Hill, and Gorman 2020).
This computer lab marks the final in our series of labs focusing on data visualisation in RStudio.
Today 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.
By the end of this lab, you should be able to create plotly
graphs with customized sliders, plotly
graphs containing multiple plots, plotly
graphs with dropdown buttons to switch between data visualizations, and have a decent idea of how to animate plotly
graphs.
Note: Before you begin this lab, make sure you have read over Section 5 of the Data Visualisation in R supplement on adding buttons to 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, please install them before continuing.
Recall in the second Data Science computer lab 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.
We will introduce some new plotly
features using this scatter plot as a base. The code for this plot 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")
penguins_scatter <- penguins_scatter %>%
layout(title = "Scatter Plot of Penguin Data",
legend=list(title=list(text='Sex')),
xaxis = list(title = "Penguin Body Mass (grams)"),
yaxis = list(title = "Penguin Flipper Length (mm)"))
Run this code in RStudio now.
Adding Range Sliders to plotly
Plots
💻 In the third Data Science computer lab, we gained some experience using the pipe operator and the layout
function.
Let’s use those skills now to add some fancier elements to our plotly
graphs. First, we will take a look at adding a range slider to our plot.
💻 Suppose that we would like to add a range slider to the x-axis of our penguins_scatter
scatter plot from 1.2. 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 a plot using the function rangeslider()
.
Use the pipe operator to add a range slider to the penguins_scatter
.
💻 If your code has worked, your scatter plot should now include a range slider (as 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 difficult 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, but for now this one line of code is sufficient for our purposes.
🎧 Online students
💬 Volunteer to share your screen to show and describe your plotly plot. Highlight any issues you have encountered while making the plot.
Creating animated plotly
Plots in RStudio
💻 Another 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 simply frame = ...
.
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.
Use the frame = ...
argument to add the year
variable into our penguins_scatter
scatter plot from 1.2. You may want to assign the new plot to a new object - e.g. penguins_scatter_anim
.
Note: You will of course need to replace the ...
’s with the appropriate code.
💻 If your code has worked, your plot should now include an animation option (as shown 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.
Also, 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?
🎧 Online students
💬 Comment on any differences you observe between the different species.
Creating Combined plotly
Plots in RStudio
💻 Over the course of the data science data visualisation labs, we have created interactive histograms, scatter plots, box plots and violin plots.
Suppose that we would like to present multiple data visualisations of the penguins
data together in the one graph. One approach we could use for this would be to use the plotly
subplot
function.
💻 To begin, suppose we would like to combine an interactive histogram and an interactive scatter plot focusing on the recorded penguins
body mass values.
Run the code in the code chunk below to prepare the histogram and reset the scatter plot details:
penguin_hist <- plot_ly(data = penguins,
x = ~body_mass_g,
color = ~island,
type = "histogram", alpha = 0.6)
penguin_hist <- penguin_hist %>% layout(barmode ="overlay")
penguins_scatter <- plot_ly(data = penguins,
x = ~body_mass_g, y = ~flipper_length_mm,
color = ~sex, colors = "Set1",
type = "scatter", mode = "markers")
Next, take a look at the R code below:
penguin_combined_plots <- subplot(penguins_scatter, 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_scatter
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).
💻 If you now run this object penguin_combined_plots
, you should obtain the set of two plots, in a single view (as shown below):
penguin_combined_plots
Note that the two plots are still completely 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 re-specified, as above.
💻 Note that the automatically generated legend title of the combined subplot
shown above in 4.2 is not completely accurate. Add a more informative legend to this subplot
.
Hint: You will have to add an argument to the layout
section of the code.
💻 Using the information from 4.1, try to combine the scatter plot from 1.2 with both the histogram from 4.1, and the box plots you created in section 2.7 of the third Data Science computer lab.
Make sure your combined subplot
has appropriate axes labels and legend.
Hint: You can check the solutions for Data Science Computer Lab 3, or create a new object for the box plots using your plotly
skills.
🎧 Online students
💬 Volunteer to share your screen to show and describe your plotly plot. Highlight any issues you have encountered while making the plot.
Extension: Adding Buttons to plotly
Plots
💻 So far, hopefully the coding in this lab has not been too intense. That’s about to change.
While subplots
are useful for combining a couple of different plots, they can become unwieldy when we consider too many plots at once.
In this final section, we’ll look at an alternative approach - buttons
.
We can add buttons
to a plotly
plot, which (when clicked) will allow us to shift between different presentations of our data.
Please note - before you begin this section, make sure you have completed the Data Science Computer Lab 3 and have read through Section 5 of the Data Visualisation in R supplement.
Note: It may also be helpful to have these open in a separate tab, so that you can refer to them as you work through this section.
💻 It is worth noting that plotly
graphs incorporating buttons
can run into difficulties when trying to switch between different data sets.
To keep things at an appropriate level of difficulty, we will focus on presenting data for one variable from the penguins
data set at a time using our buttons
-enabled plots.
💻 Good work! Let’s take a step back, and consider some smaller modifications we can make to our code in 5.4.
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 our penguins_plots
object code to type = "dropdown"
(which makes sense).
Try making this change now, and check the results.
Note: Despite changing the type = "buttons"
code, we keep the buttons = list(...)
specification, this doesn’t need to change.
💻 You will have noticed that the penguins_plots
’s histogram looks a little different to the one in our combined subplot
from 4.2. This is because we have not specified here 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 penguins_plots
plot.
Try inserting the barmode ="overlay"
command into your code for our new penguins_plots
object now.
Hint: It doesn’t need to be added within the updatemenus
function.
💻 Next, to really appreciate the benefit of the buttons
approach over the subplot
approach, let’s add a third plot in our 5.2 code.
Using your code from 5.3 as a guide, add code to the layout
specifications of the penguins_plots
object so that the penguins
body mass data can, as a third option, also be presented in violin plots.
Note: To ensure the box plots are shown within the violin plots, you will need to also add the code box = list(visible = T )
in your violin plot specifications.
💻 Now that you feel more comfortable using buttons
, try to complete the following steps:
- Switch the order of
buttons
around so that violin plots are shown first and the histogram is shown last.
- Change the data shown from being body mass data, to bill length data, and adjust the title accordingly.
- Colour the observations by
species
, not sex
.
- Add a rangeslider to your plots.
💻 As you can see, when we are dealing with plots presenting multiple variables, it may be better to use subplots
, while if we are dealing with different plots of the one variable, it may be better to use buttons
. Perhaps a mixture is best.
Do you have a preference for subplots
or buttons
?
🎧 Online students
💬 Volunteer to share your screen to show and describe your plotly plot. Highlight any issues you have encountered while making the plot.
Well done! There was a lot of content today. You have come a long way from that first base R histogram you created back in the second Data Science Computer Lab.
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.
LS0tDQp0aXRsZTogIlNUTTEwMDE6IENvbXB1dGVyIExhYiA0QiINCm91dHB1dDoNCiAgYm9va2Rvd246Omh0bWxfZG9jdW1lbnQyOiANCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZmxvYXQ6IHRydWUNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgdGhlbWU6IHJlYWRhYmxlDQogICAgY29kZV9mb2xkaW5nOiBzaG93DQpiaWJsaW9ncmFwaHk6IFNUTTEwMDFfRFNfQ0xfcmVmZXJlbmNlcy5iaWIgDQpsaW5rLWNpdGF0aW9uczogeWVzDQotLS0NCg0KPHN0eWxlPg0KI1RPQyB7DQogIGJhY2tncm91bmQ6IHVybCgiaHR0cHM6Ly93d3cubGF0cm9iZS5lZHUuYXUvX21lZGlhL2xhLXRyb2JlLWFwaS92NS9pbWcvbG9nby5zdmciKTsNCiAgYmFja2dyb3VuZC1zaXplOiBjb250YWluOw0KICBwYWRkaW5nLXRvcDogODBweCAhaW1wb3J0YW50Ow0KICBiYWNrZ3JvdW5kLXJlcGVhdDogbm8tcmVwZWF0Ow0KfQ0KPC9zdHlsZT4NCg0KIyMjIERhdGEgU2NpZW5jZSBTdHJlYW0gey19DQoNCg0KIyMjIFRvcGljIDRCOiBEYXRhIFZpc3VhbGlzYXRpb24gSUlJIHstfQ0KDQo8YnI+DQoNCldlbGNvbWUgdG8gdGhlIGZvdXJ0aCBjb21wdXRlciBsYWIgZm9yIHRoZSBEYXRhIFNjaWVuY2Ugc3RyZWFtIG9mIFNUTTEwMDEuDQoNCkluIHRoZSBbc2Vjb25kXShodHRwczovL3JwdWJzLmNvbS9MVFVfU1RNMTAwMS9EU01DTDIpIGFuZCBbdGhpcmRdKGh0dHBzOi8vcnB1YnMuY29tL0xUVV9TVE0xMDAxL0RTTUNMMykgIERhdGEgU2NpZW5jZSBzdHJlYW0gY29tcHV0ZXIgbGFicyB3ZSBmYW1pbGlhcmlzZWQgb3Vyc2VsdmVzIHdpdGggdGhlIGBwbG90bHlgIGZ1bmN0aW9uLCBhbmQgbWFkZSBzb21lIGluZm9ybWF0aXZlIGFuZCBpbnRlcmFjdGl2ZSBwbG90cyBvZiBkYXRhIGZyb20gdGhlIGBwYWxtZXJwZW5ndWluc2AgcGFja2FnZSBbQHBlbmd1aW5zXS4NCg0KVGhpcyBjb21wdXRlciBsYWIgbWFya3MgdGhlIGZpbmFsIGluIG91ciBzZXJpZXMgb2YgbGFicyBmb2N1c2luZyBvbiBkYXRhIHZpc3VhbGlzYXRpb24gaW4gUlN0dWRpby4NCg0KVG9kYXkgd2Ugd2lsbCBmb2N1cyBvbiBmdXJ0aGVyIGRldmVsb3Bpbmcgb3VyIHNraWxscyB3aXRoIGBwbG90bHlgIFtAcGxvdGx5XSwgYW5kIGNvdmVyIGFkZGluZyBjdXN0b20gY29udHJvbHMgYW5kIGFuaW1hdGlvbnMgdG8gb3VyIGBwbG90bHlgIHBsb3RzLiBUaGUgY29kaW5nIGluIHRoaXMgbGFiIGlzIGEgbGl0dGxlIG1vcmUgaW50ZW5zZSB0aGFuIGluIHByZXZpb3VzIHdlZWtzLCBidXQgd2Ugd2lsbCB0YWtlIG91ciB0aW1lLCBhbmQgZ28gdGhyb3VnaCBlYWNoIG9mIHRoZSBzdGVwcyBzbG93bHkuIA0KDQpCeSB0aGUgZW5kIG9mIHRoaXMgbGFiLCB5b3Ugc2hvdWxkIGJlIGFibGUgdG8gY3JlYXRlIGBwbG90bHlgIGdyYXBocyB3aXRoICoqY3VzdG9taXplZCBzbGlkZXJzKiosIGBwbG90bHlgIGdyYXBocyBjb250YWluaW5nICoqbXVsdGlwbGUgcGxvdHMqKiwgYHBsb3RseWAgZ3JhcGhzIHdpdGggKipkcm9wZG93biBidXR0b25zKiogdG8gc3dpdGNoIGJldHdlZW4gZGF0YSB2aXN1YWxpemF0aW9ucywgYW5kIGhhdmUgYSBkZWNlbnQgaWRlYSBvZiBob3cgdG8gKiphbmltYXRlKiogYHBsb3RseWAgZ3JhcGhzLg0KDQoqTm90ZTogQmVmb3JlIHlvdSBiZWdpbiB0aGlzIGxhYiwgbWFrZSBzdXJlIHlvdSBoYXZlIHJlYWQgb3ZlciBbU2VjdGlvbiA1IG9mIHRoZSBEYXRhIFZpc3VhbGlzYXRpb24gaW4gUiBzdXBwbGVtZW50XShodHRwczovL2Jvb2tkb3duLm9yZy9yZWhrL3N0bTEwMDFfZHNtX2RhdGFfdmlzdWFsaXNhdGlvbl9pbl9yL2FkdmFuY2VkLXBsb3RseS1jb21wdXRlci1sYWItNGIuaHRtbCkgb24gYWRkaW5nIGJ1dHRvbnMgdG8gYHBsb3RseWAgZ3JhcGhzLioNCg0KPGJyPg0KDQojIFByZXBhcmF0aW9uIHsjbG9hZGluZ30NCg0KYHIgZW1vOjpqaSgiaG91c2Vfd2l0aF9nYXJkZW4iKWAgQmVmb3JlIHdlIGJlZ2luIG91ciB3b3JrLCB3ZSB3aWxsIG5lZWQgdG8gY2Fycnkgb3V0IHNvbWUgaW5pdGlhbCBwcmVwYXJhdGlvbnMuDQoNCiMjDQoNClRvIGJlZ2luLCB3ZSB3aWxsIG5lZWQgdG8gbG9hZCBhbGwgdGhlIHJlcXVpc2l0ZSBwYWNrYWdlcy4NCkJ5IG5vdywgeW91IHNob3VsZCBoYXZlIHRoZSBgcGFsbWVycGVuZ3VpbnNgIGFuZCBgcGxvdGx5YCBwYWNrYWdlcyBpbnN0YWxsZWQgb24geW91ciBzeXN0ZW0uDQpPcGVuIHVwIFJTdHVkaW8gYW5kIGxvYWQgdGhlc2UgcGFja2FnZXMgbm93Lg0KDQoqSWYgZm9yIHNvbWUgcmVhc29uIHlvdSBkbyBub3QgaGF2ZSBvbmUgb3IgYm90aCBvZiB0aGVzZSBwYWNrYWdlcyBpbnN0YWxsZWQsIHBsZWFzZSBpbnN0YWxsIHRoZW0gYmVmb3JlIGNvbnRpbnVpbmcuKg0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IFQsIGluY2x1ZGUgPSBGfQ0KbGlicmFyeShwYWxtZXJwZW5ndWlucykNCmxpYnJhcnkocGxvdGx5KQ0KYGBgDQoNCiMjIHsjc2ltcGxlc2NhdHRlcn0NCg0KUmVjYWxsIGluIHRoZSBbc2Vjb25kIERhdGEgU2NpZW5jZSBjb21wdXRlciBsYWJdKGh0dHBzOi8vcnB1YnMuY29tL0xUVV9TVE0xMDAxL0RTTUNMMikgdGhhdCB3ZSBjcmVhdGVkIGEgc2ltcGxlIHNjYXR0ZXIgcGxvdCB1c2luZyB0aGUgYGJvZHlfbWFzc19nYCBhbmQgYGZsaXBwZXJfbGVuZ3RoX21tYCB2YXJpYWJsZXMgZnJvbSB0aGUgYHBlbmd1aW5zYCBkYXRhIHNldC4gV2UgYWxzbyB1c2VkIGRpZmZlcmVudCBjb2xvdXJzIHRvIGRpc3Rpbmd1aXNoIGJldHdlZW4gbWFsZSBhbmQgZmVtYWxlIHBlbmd1aW5zLiANCg0KV2Ugd2lsbCBpbnRyb2R1Y2Ugc29tZSBuZXcgYHBsb3RseWAgZmVhdHVyZXMgdXNpbmcgdGhpcyBzY2F0dGVyIHBsb3QgYXMgYSBiYXNlLiBUaGUgY29kZSBmb3IgdGhpcyBwbG90IGlzIHJlcHJvZHVjZWQgYmVsb3c6DQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gVCwgZWNobyA9IFR9DQpwZW5ndWluc19zY2F0dGVyIDwtIHBsb3RfbHkoZGF0YSA9IHBlbmd1aW5zLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB4ID0gfmJvZHlfbWFzc19nLCB5ID0gfmZsaXBwZXJfbGVuZ3RoX21tLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IH5zZXgsIGNvbG9ycyA9ICJTZXQxIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlID0gInNjYXR0ZXIiLCBtb2RlID0gIm1hcmtlcnMiKQ0KDQpwZW5ndWluc19zY2F0dGVyIDwtIHBlbmd1aW5zX3NjYXR0ZXIgJT4lIA0KICAgICAgICAgICAgICAgICAgICAgICAgbGF5b3V0KHRpdGxlID0gIlNjYXR0ZXIgUGxvdCBvZiBQZW5ndWluIERhdGEiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmQ9bGlzdCh0aXRsZT1saXN0KHRleHQ9J1NleCcpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSAiUGVuZ3VpbiBCb2R5IE1hc3MgKGdyYW1zKSIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHlheGlzID0gbGlzdCh0aXRsZSA9ICJQZW5ndWluIEZsaXBwZXIgTGVuZ3RoIChtbSkiKSkNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICANCmBgYA0KDQpSdW4gdGhpcyBjb2RlIGluIFJTdHVkaW8gbm93Lg0KDQojIEFkZGluZyBSYW5nZSBTbGlkZXJzIHRvIGBwbG90bHlgIFBsb3RzIHsjc2xpZGVyfSANCg0KYHIgZW1vOjpqaSgiY29tcHV0ZXIiKWAgSW4gdGhlIFt0aGlyZCBEYXRhIFNjaWVuY2UgY29tcHV0ZXIgbGFiXShodHRwczovL3JwdWJzLmNvbS9MVFVfU1RNMTAwMS9EU01DTDMpLCB3ZSBnYWluZWQgc29tZSBleHBlcmllbmNlIHVzaW5nIHRoZSBwaXBlIG9wZXJhdG9yIGFuZCB0aGUgYGxheW91dGAgZnVuY3Rpb24uDQoNCkxldCdzIHVzZSB0aG9zZSBza2lsbHMgbm93IHRvIGFkZCBzb21lIGZhbmNpZXIgZWxlbWVudHMgdG8gb3VyIGBwbG90bHlgIGdyYXBocy4gRmlyc3QsIHdlIHdpbGwgdGFrZSBhIGxvb2sgYXQgYWRkaW5nIGEgKipyYW5nZSBzbGlkZXIqKiB0byBvdXIgcGxvdC4NCg0KIyMgeyNzbGlkZXJzdGFydH0NCg0KYHIgZW1vOjpqaSgiY29tcHV0ZXIiKWAgU3VwcG9zZSB0aGF0IHdlIHdvdWxkIGxpa2UgdG8gYWRkIGEgKipyYW5nZSBzbGlkZXIqKiB0byB0aGUgeC1heGlzIG9mIG91ciBgcGVuZ3VpbnNfc2NhdHRlcmAgc2NhdHRlciBwbG90IGZyb20gXEByZWYoc2ltcGxlc2NhdHRlcikuIEEgcmFuZ2Ugc2xpZGVyIGNhbiBiZSB1c2VkIHRvIGR5bmFtaWNhbGx5IHNlbGVjdCBhIHN1YnNlY3Rpb24gb2Ygb3VyIHBsb3QsIGluIGEgc2ltaWxhciwgYnV0IG1vcmUgY29udHJvbGxlZCB3YXkgdG8gbGVmdC1jbGlja2luZyBhbmQgZHJhZ2dpbmcgYSBib3ggb3ZlciBvdXIgcGxvdCB0byB6b29tIGluIG9uIGEgc2VjdGlvbi4NCg0KV2UgY2FuIGFkZCBhIHJhbmdlIHNsaWRlciB0byB0aGUgeC1heGlzIG9mIGEgcGxvdCB1c2luZyB0aGUgZnVuY3Rpb24gYHJhbmdlc2xpZGVyKClgLg0KDQpVc2UgdGhlIHBpcGUgb3BlcmF0b3IgdG8gYWRkIGEgcmFuZ2Ugc2xpZGVyIHRvIHRoZSBgcGVuZ3VpbnNfc2NhdHRlcmAuDQoNCiMjDQoNCmByIGVtbzo6amkoImNvbXB1dGVyIilgIElmIHlvdXIgY29kZSBoYXMgd29ya2VkLCB5b3VyIHNjYXR0ZXIgcGxvdCBzaG91bGQgbm93IGluY2x1ZGUgYSByYW5nZSBzbGlkZXIgKGFzIHNob3duIGJlbG93KS4gDQoNClRyeSBsZWZ0LWNsaWNraW5nIGFuZCBkcmFnZ2luZyB0aGUgYmFycyBvbiB0aGUgc2xpZGVyIGVuZHBvaW50cy4NCg0KKkhpbnQ6IFlvdSBjYW4gYWx3YXlzIGNoZWNrIHRoZSBgQ29kZWAgYm94IGJlbG93IGlmIHlvdXIgY29kZSBpcyBub3Qgd29ya2luZy4qDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUiLCBldmFsID0gVCwgZWNobyA9IFQsIHdhcm5pbmcgPSBGfQ0KcGVuZ3VpbnNfc2NhdHRlciAlPiUgcmFuZ2VzbGlkZXIoKQ0KIyBOb3QgdG9vIGRpZmZpY3VsdCBzbyBmYXIhDQpgYGANCg0KIyMNCg0KYHIgZW1vOjpqaSgiY29tcHV0ZXIiKWAgSW5pdGlhbGx5LCBhZGRpbmcgYSByYW5nZSBzbGlkZXIgdG8geW91ciBwbG90IG1pZ2h0IGhhdmUgc2VlbWVkIHZlcnkgZGlmZmljdWx0Lg0KSG93ZXZlciwgYXMgeW91IGNhbiBzZWUsIGRlc3BpdGUgYmVpbmcgYW4gaW1wcmVzc2l2ZSBhZGRpdGlvbiBpdCdzIGFjdHVhbGx5IHZlcnkgc3RyYWlnaHRmb3J3YXJkIHRvIGltcGxlbWVudC4NCg0KT2YgY291cnNlLCB0aGVyZSBhcmUgdmFyaW91cyBhZGRpdGlvbmFsIGFkanVzdG1lbnRzIHRoYXQgd2UgY291bGQgbWFrZSB0byBvdXIgcmFuZ2Ugc2xpZGVyLCBidXQgZm9yIG5vdyB0aGlzIG9uZSBsaW5lIG9mIGNvZGUgaXMgc3VmZmljaWVudCBmb3Igb3VyIHB1cnBvc2VzLg0KDQo8YnI+DQoNCjxkZXRhaWxzPg0KICA8c3VtbWFyeT5gciBlbW86OmppKCJoZWFkcGhvbmVzIilgICoqT25saW5lIHN0dWRlbnRzKio8L3N1bW1hcnk+DQpgciBlbW86OmppKCJzcGVlY2hfYmFsbG9vbiIpYCAgVm9sdW50ZWVyIHRvIHNoYXJlIHlvdXIgc2NyZWVuIHRvIHNob3cgYW5kIGRlc2NyaWJlIHlvdXIgcGxvdGx5IHBsb3QuIEhpZ2hsaWdodCBhbnkgaXNzdWVzIHlvdSBoYXZlIGVuY291bnRlcmVkIHdoaWxlIG1ha2luZyB0aGUgcGxvdC4gDQo8L2RldGFpbHM+IA0KDQo8YnI+DQoNCg0KIyBDcmVhdGluZyBhbmltYXRlZCBgcGxvdGx5YCBQbG90cyBpbiBSU3R1ZGlvIHsjYW5pbWF0aW9uc30NCg0KYHIgZW1vOjpqaSgiY29tcHV0ZXIiKWAgQW5vdGhlciBpbXByZXNzaXZlIGFkZGl0aW9uIHdlIGNhbiBtYWtlIHRvIG91ciBwbG90IGlzIHRvIHR1cm4gaXQgaW50byBhbiBhbmltYXRpb24uDQoNCllvdSBtaWdodCByZWNhbGwgdGhhdCBvbmUgb2YgdGhlIHZhcmlhYmxlcyBpbiB0aGUgYHBlbmd1aW5zYCBkYXRhIHNldCB3aGljaCB3ZSBoYXZlbid0IHJlYWxseSBjb25zaWRlcmVkIHNvIGZhciBpcyB0aGUgYHllYXJgIHZhcmlhYmxlIC0gbmFtZWx5LCB3ZSBoYXZlIHBlbmd1aW4gZGF0YSByZWNvcmRlZCBmb3IgdGhlIHllYXJzIDIwMDcsIDIwMDggYW5kIDIwMDkuDQoNClN1cHBvc2Ugd2Ugd291bGQgbGlrZSB0byBzZWUgaG93IHRoZSBgYm9keV9tYXNzX2dgIGFuZCBgYmlsbF9sZW5ndGhfbW1gIHZhbHVlcyBvZiB0aGUgbWFsZSBhbmQgZmVtYWxlIHBlbmd1aW5zIGNoYW5nZXMgb3ZlciB0aGUgeWVhcnMuIFdlIGFscmVhZHkgaGF2ZSB0aGlzIGluZm9ybWF0aW9uIHN0b3JlZCBhd2F5IGluIHRoZSBgcGVuZ3VpbnNgIGRhdGEgc2V0LCBidXQgd2UgaGF2ZW4ndCB2aXN1YWxpc2VkIGl0IHlldC4NCg0KQ291bGQgd2Ugc29tZWhvdyBtb2RpZnkgb3VyIHNjYXR0ZXIgcGxvdCB0byBzaG93IGRhdGEgZm9yIGVhY2ggeWVhciwgYW5kIGR5bmFtaWNhbGx5IHN3aXRjaCBiZXR3ZWVuIHllYXJzIG9uIGNvbW1hbmQ/IElzIHN1Y2ggYSB0aGluZyBldmVuIHBvc3NpYmxlPyBXaHksIHdpdGggYHBsb3RseWAsIHllcyBpdCBpcyENCg0KIyMNCg0KYHIgZW1vOjpqaSgiY29tcHV0ZXIiKWAgQWRkaW5nIGFuaW1hdGlvbnMgdG8gYSBgcGxvdGx5YCBwbG90IGlzIHN1cnByaXNpbmdseSBlYXN5LCBidXQgd2UgbmVlZCB0byBlbnN1cmUgdGhhdCBvdXIgZGF0YSBhbmQgY29kZSBpcyBzZXQgdXAgcHJvcGVybHkuIEZvcnR1bmF0ZWx5LCBpbiB0aGlzIGluc3RhbmNlIHRoZSBgcGVuZ3VpbnNgIGRhdGEgc2V0IHdoaWNoIHdlIGFyZSB1c2luZyBhbHJlYWR5IGNvbnRhaW5zIHRoZSBpbmZvcm1hdGlvbiB3ZSB3b3VsZCBsaWtlIHRvIHVzZSBmb3Igb3VyIGFuaW1hdGlvbi4gDQoNClRoZSBhcmd1bWVudCB3ZSB3aWxsIHVzZSB0byB0dXJuIG91ciBzY2F0dGVyIHBsb3QgaW50byBhbiBhbmltYXRlZCBwbG90IGlzIHNpbXBseSBgZnJhbWUgPSAuLi5gLg0KV2UgbmVlZCB0byBpbmNsdWRlIHRoaXMgaW5zaWRlIG91ciBgcGxvdGx5KClgIGZ1bmN0aW9uLCBpbiBhIHNpbWlsYXIgZmFzaGlvbiB0byBob3cgd2UgdXNlIGB4PSAuLi5gIGFuZCBgeT0gLi4uYCB3aGVuIGFzc2lnbmluZyB0aGUgZGF0YSBmb3Igb3VyIGB4YCBhbmQgYHlgIHZhcmlhYmxlcy4NCg0KVXNlIHRoZSBgZnJhbWUgPSAuLi5gIGFyZ3VtZW50IHRvIGFkZCB0aGUgYHllYXJgIHZhcmlhYmxlIGludG8gb3VyIGBwZW5ndWluc19zY2F0dGVyYCBzY2F0dGVyIHBsb3QgZnJvbSBcQHJlZihzaW1wbGVzY2F0dGVyKS4gWW91IG1heSB3YW50IHRvIGFzc2lnbiB0aGUgbmV3IHBsb3QgdG8gYSBuZXcgb2JqZWN0IC0gZS5nLiBgcGVuZ3VpbnNfc2NhdHRlcl9hbmltYC4NCg0KKk5vdGU6IFlvdSB3aWxsIG9mIGNvdXJzZSBuZWVkIHRvIHJlcGxhY2UgdGhlIGAuLi5gJ3Mgd2l0aCB0aGUgYXBwcm9wcmlhdGUgY29kZS4qDQoNCiMjDQoNCmByIGVtbzo6amkoImNvbXB1dGVyIilgIElmIHlvdXIgY29kZSBoYXMgd29ya2VkLCB5b3VyIHBsb3Qgc2hvdWxkIG5vdyBpbmNsdWRlIGFuIGFuaW1hdGlvbiBvcHRpb24gKGFzIHNob3duIGJlbG93KToNCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBULCBlY2hvID0gRiwgd2FybmluZyA9IEYsIGZpZy5hbGlnbj0nY2VudGVyJ30NCnBlbmd1aW5zX3NjYXR0ZXJfYW5pbSA8LSBwbG90X2x5KGRhdGEgPSBwZW5ndWlucywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB4ID0gfmJvZHlfbWFzc19nLCB5ID0gfmZsaXBwZXJfbGVuZ3RoX21tLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gfnNleCwgY29sb3JzID0gIlNldDEiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRleHQgPSB+c3BlY2llcywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZyYW1lID0gfnllYXIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlID0gInNjYXR0ZXIiLCBtb2RlID0gIm1hcmtlcnMiKQ0KcGVuZ3VpbnNfc2NhdHRlcl9hbmltDQpgYGANCg0KSWYgeW91IGhhdmVuJ3QgYWxyZWFkeSwgdHJ5IGNsaWNraW5nIG9uIHRoZSBgUGxheWAgYnV0dG9uLCB0byB3YXRjaCB0aGUgYW5pbWF0aW9uIHVuZm9sZC4NCklmIGl0J3MgYSBsaXR0bGUgZmFzdCwgeW91IGNhbiBhbHNvIGNsaWNrIGFuZCBkcmFnIHRoZSBjaXJjbGUgaW4gdGhlIHNsaWRlciB0byBjaGFuZ2UgYmV0d2VlbiB5ZWFycy4NCg0KIyMNCg0KYHIgZW1vOjpqaSgiY29tcHV0ZXIiKWAgTmV4dCwgaW5zdGVhZCBvZiB1c2luZyB0aGUgYHllYXJgIHZhcmlhYmxlLCBjcmVhdGUgYSBzY2F0dGVyIHBsb3QgYW5pbWF0aW9uIHRoYXQgY3ljbGVzIHRocm91Z2ggdGhlIGRpZmZlcmVudCBgc3BlY2llc2Agb2YgcGVuZ3VpbiBpbiB0aGUgYHBlbmd1aW5zYCBkYXRhIHNldC4gDQoNCkFsc28sIGNoYW5nZSB5b3VyIGhvdmVyIHRleHQgZnJvbSBzaG93aW5nIHRoZSBzcGVjaWVzIG9mIHBlbmd1aW4sIHRvIHNob3dpbmcgdGhlIHllYXIgdGhlIGRhdGEgd2FzIHJlY29yZGVkLg0KDQpXaGF0IGRvIHlvdSBub3RpY2UgYWJvdXQgdGhlIGRpZmZlcmVudCBzcGVjaWVzPw0KDQo8YnI+DQoNCjxkZXRhaWxzPg0KICA8c3VtbWFyeT5gciBlbW86OmppKCJoZWFkcGhvbmVzIilgICoqT25saW5lIHN0dWRlbnRzKio8L3N1bW1hcnk+DQpgciBlbW86OmppKCJzcGVlY2hfYmFsbG9vbiIpYCAgQ29tbWVudCBvbiBhbnkgZGlmZmVyZW5jZXMgeW91IG9ic2VydmUgYmV0d2VlbiB0aGUgZGlmZmVyZW50IHNwZWNpZXMuDQo8L2RldGFpbHM+IA0KDQo8YnI+DQoNCiMgQ3JlYXRpbmcgQ29tYmluZWQgYHBsb3RseWAgUGxvdHMgaW4gUlN0dWRpbyB7I3N1YnBsb3RzfQ0KDQpgciBlbW86OmppKCJjb21wdXRlciIpYCBPdmVyIHRoZSBjb3Vyc2Ugb2YgdGhlIGRhdGEgc2NpZW5jZSBkYXRhIHZpc3VhbGlzYXRpb24gbGFicywgd2UgaGF2ZSBjcmVhdGVkIGludGVyYWN0aXZlIGhpc3RvZ3JhbXMsIHNjYXR0ZXIgcGxvdHMsIGJveCBwbG90cyBhbmQgdmlvbGluIHBsb3RzLg0KDQpTdXBwb3NlIHRoYXQgd2Ugd291bGQgbGlrZSB0byBwcmVzZW50IG11bHRpcGxlIGRhdGEgdmlzdWFsaXNhdGlvbnMgb2YgdGhlIGBwZW5ndWluc2AgZGF0YSB0b2dldGhlciBpbiB0aGUgb25lIGdyYXBoLiBPbmUgYXBwcm9hY2ggd2UgY291bGQgdXNlIGZvciB0aGlzIHdvdWxkIGJlIHRvIHVzZSB0aGUgYHBsb3RseWAgYHN1YnBsb3RgIGZ1bmN0aW9uLg0KDQojIyB7I3N1YnBsb3R3YWxrdGhyb3VnaH0NCg0KYHIgZW1vOjpqaSgiY29tcHV0ZXIiKWAgVG8gYmVnaW4sIHN1cHBvc2Ugd2Ugd291bGQgbGlrZSB0byBjb21iaW5lIGFuIGludGVyYWN0aXZlIGhpc3RvZ3JhbSBhbmQgYW4gaW50ZXJhY3RpdmUgc2NhdHRlciBwbG90IGZvY3VzaW5nIG9uIHRoZSByZWNvcmRlZCBgcGVuZ3VpbnNgIGJvZHkgbWFzcyB2YWx1ZXMuDQoNClJ1biB0aGUgY29kZSBpbiB0aGUgY29kZSBjaHVuayBiZWxvdyB0byBwcmVwYXJlIHRoZSBoaXN0b2dyYW0gYW5kIHJlc2V0IHRoZSBzY2F0dGVyIHBsb3QgZGV0YWlsczoNCiAgDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIiwgZXZhbCA9IFQsIGVjaG8gPSBULCB3YXJuaW5nID0gRiwgbWVzc2FnZSA9IEYsIGZpZy5hbGlnbiA9ICJjZW50ZXIifQ0KcGVuZ3Vpbl9oaXN0IDwtIHBsb3RfbHkoZGF0YSA9IHBlbmd1aW5zLCANCiAgICAgICAgICAgICAgICAgICAgICAgIHggPSB+Ym9keV9tYXNzX2csIA0KICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSB+aXNsYW5kLCANCiAgICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAiaGlzdG9ncmFtIiwgYWxwaGEgPSAwLjYpDQoNCnBlbmd1aW5faGlzdCA8LSBwZW5ndWluX2hpc3QgJT4lIGxheW91dChiYXJtb2RlID0ib3ZlcmxheSIpDQoNCnBlbmd1aW5zX3NjYXR0ZXIgPC0gcGxvdF9seShkYXRhID0gcGVuZ3VpbnMsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIHggPSB+Ym9keV9tYXNzX2csIHkgPSB+ZmxpcHBlcl9sZW5ndGhfbW0sIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gfnNleCwgY29sb3JzID0gIlNldDEiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAic2NhdHRlciIsIG1vZGUgPSAibWFya2VycyIpDQpgYGANCg0KTmV4dCwgdGFrZSBhIGxvb2sgYXQgdGhlIFIgY29kZSBiZWxvdzoNCiAgDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IFQsIGVjaG8gPSBULCB3YXJuaW5nID0gRiwgbWVzc2FnZSA9IEZ9DQpwZW5ndWluX2NvbWJpbmVkX3Bsb3RzIDwtIHN1YnBsb3QocGVuZ3VpbnNfc2NhdHRlciwgcGVuZ3Vpbl9oaXN0LCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBucm93cyA9IDIsIG1hcmdpbiA9IDAuMDUpIA0KcGVuZ3Vpbl9jb21iaW5lZF9wbG90cyA8LSBwZW5ndWluX2NvbWJpbmVkX3Bsb3RzICU+JSANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYXlvdXQodGl0bGUgPSAiUGFsbWVyIFBlbmd1aW4gRGF0YSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHhheGlzID0gbGlzdCh0aXRsZSA9ICdib2R5X21hc3NfZycpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeWF4aXMgPSBsaXN0KHRpdGxlID0gImZsaXBwZXJfbGVuZ3RoX21tIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHhheGlzMiA9IGxpc3QodGl0bGUgPSAnYm9keV9tYXNzX2cnKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHlheGlzMiA9IGxpc3QodGl0bGUgPSAiY291bnQiKSkNCmBgYA0KDQpOb3RlIHRoYXQgaGVyZToNCiAgDQoqIFdlIGFyZSB1c2luZyB0aGUgYHN1YnBsb3RgIGNvbW1hbmQgdG8gcGxvdCB0aGUgYHBlbmd1aW5zX3NjYXR0ZXJgIGFuZCBgcGVuZ3Vpbl9oaXN0YCBwbG90cyB0b2dldGhlci4NCiogVGhlIGBucm93cyA9IDJgIGFyZ3VtZW50IHRlbGxzIFIgdG8gcHJvZHVjZSB0aGVzZSBwbG90cyBpbiAyIHJvd3MuDQoqIFRoZSBgbWFyZ2luID0gMC4wNWAgYXJndW1lbnQgdGVsbHMgUiB0byBsZWF2ZSBhIHNtYWxsIG1hcmdpbiBiZXR3ZWVuIHRoZSB0d28gcGxvdHMuDQoqIFRoZSBzdWJzZXF1ZW50IGxpbmVzIG9mIGNvZGUgYXJlIHVzZWQgdG8gYWRkIGEgdGl0bGUgdG8gb3VyIHNlbGVjdGlvbiBvZiBwbG90cywgYW5kIGFkZCBheGVzIGxhYmVscyB0byB0aGUgcGxvdHMgLSBub3RlIHRoYXQgd2UgdXNlIGB4YXhpc2AgdG8gZGVmaW5lIHRoZSB4LWF4aXMgbGFiZWwgZm9yIHRoZSBmaXJzdCBwbG90LCBhbmQgYHhheGlzMmAgdG8gZGVmaW5lIHRoZSB4LWF4aXMgbGFiZWwgZm9yIHRoZSBzZWNvbmQgcGxvdCAoYW5kIHNpbWlsYXJseSBmb3IgdGhlIHktYXhlcykuDQoNCiMjIHsjY29tYmluZWR9DQoNCmByIGVtbzo6amkoImNvbXB1dGVyIilgIElmIHlvdSBub3cgcnVuIHRoaXMgb2JqZWN0IGBwZW5ndWluX2NvbWJpbmVkX3Bsb3RzYCwgeW91IHNob3VsZCBvYnRhaW4gdGhlIHNldCBvZiB0d28gcGxvdHMsIGluIGEgc2luZ2xlIHZpZXcgKGFzIHNob3duIGJlbG93KTogDQogIA0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBULCBlY2hvID0gVCwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGLCBmaWcuZGltID0gYyg4LCA4KSwgZmlnLmFsaWduID0gImNlbnRlciJ9DQpwZW5ndWluX2NvbWJpbmVkX3Bsb3RzDQpgYGANCg0KTm90ZSB0aGF0IHRoZSB0d28gcGxvdHMgYXJlIHN0aWxsIGNvbXBsZXRlbHkgaW50ZXJhY3RpdmUuIFRoZSBsZWdlbmRzIGhhdmUgYmVlbiBjb21iaW5lZCwgYW5kIGNhbiBiZSB1c2VkIHRvIGZpbHRlciB0aGUgaW5kaXZpZHVhbCBwbG90cy4NCg0KV2hpbGUgd2UgaGF2ZSBvbmx5IGNvbWJpbmVkIHR3byBwbG90cyBoZXJlLCB0aGUgYHN1YnBsb3RgIGZ1bmN0aW9uIGNhbiBiZSB1c2VkIHRvIHByZXNlbnQgc2V2ZXJhbCBwbG90cyB0b2dldGhlciwgd2hpY2ggY2FuIGJlIHBhcnRpY3VsYXJseSBpbmZvcm1hdGl2ZSB3aGVuIHlvdSB3b3VsZCBsaWtlIHRvIGRpc3BsYXkgbXVsdGlwbGUgYXNwZWN0cyBvZiB5b3VyIGRhdGEgc2ltdWx0YW5lb3VzbHkuDQoNClRoZSBvbmx5IG1ham9yIGRvd25zaWRlIG9mIHByZXNlbnRpbmcgcGxvdHMgdG9nZXRoZXIgdXNpbmcgYHN1YnBsb3RgIGlzIHRoYXQgdGhlaXIgYXhlcyBsYWJlbHMgYXJlIHJlbW92ZWQgYnkgZGVmYXVsdCwgYW5kIG11c3QgYmUgcmUtc3BlY2lmaWVkLCBhcyBhYm92ZS4NCg0KIyMNCg0KYHIgZW1vOjpqaSgiY29tcHV0ZXIiKWAgTm90ZSB0aGF0IHRoZSBhdXRvbWF0aWNhbGx5IGdlbmVyYXRlZCBsZWdlbmQgdGl0bGUgb2YgdGhlIGNvbWJpbmVkIGBzdWJwbG90YCBzaG93biBhYm92ZSBpbiBcQHJlZihjb21iaW5lZCkgaXMgbm90IGNvbXBsZXRlbHkgYWNjdXJhdGUuIEFkZCBhIG1vcmUgaW5mb3JtYXRpdmUgbGVnZW5kIHRvIHRoaXMgYHN1YnBsb3RgLg0KDQoqSGludDogWW91IHdpbGwgaGF2ZSB0byBhZGQgYW4gYXJndW1lbnQgdG8gdGhlIGBsYXlvdXRgIHNlY3Rpb24gb2YgdGhlIGNvZGUuKg0KDQojIyB7I2JveHBsb3RzdWJwbG90fQ0KDQpgciBlbW86OmppKCJjb21wdXRlciIpYCBVc2luZyB0aGUgaW5mb3JtYXRpb24gZnJvbSBcQHJlZihzdWJwbG90d2Fsa3Rocm91Z2gpLCB0cnkgdG8gY29tYmluZSB0aGUgc2NhdHRlciBwbG90IGZyb20gXEByZWYoc2ltcGxlc2NhdHRlcikgd2l0aCBib3RoIHRoZSBoaXN0b2dyYW0gZnJvbSBcQHJlZihzdWJwbG90d2Fsa3Rocm91Z2gpLCBhbmQgdGhlIGJveCBwbG90cyB5b3UgY3JlYXRlZCBpbiBzZWN0aW9uIDIuNyBvZiB0aGUgW3RoaXJkXShodHRwczovL3JwdWJzLmNvbS9MVFVfU1RNMTAwMS9EU01DTDMpICBEYXRhIFNjaWVuY2UgY29tcHV0ZXIgbGFiLg0KDQpNYWtlIHN1cmUgeW91ciBjb21iaW5lZCBgc3VicGxvdGAgaGFzIGFwcHJvcHJpYXRlIGF4ZXMgbGFiZWxzIGFuZCBsZWdlbmQuDQoNCipIaW50OiBZb3UgY2FuIGNoZWNrIHRoZSBzb2x1dGlvbnMgZm9yIFtEYXRhIFNjaWVuY2UgQ29tcHV0ZXIgTGFiIDNdKGh0dHBzOi8vcnB1YnMuY29tL0xUVV9TVE0xMDAxL0RTTUNMM19Tb2wpLCBvciBjcmVhdGUgYSBuZXcgb2JqZWN0IGZvciB0aGUgYm94IHBsb3RzIHVzaW5nIHlvdXIgYHBsb3RseWAgc2tpbGxzLioNCg0KPGJyPg0KDQo8ZGV0YWlscz4NCiAgPHN1bW1hcnk+YHIgZW1vOjpqaSgiaGVhZHBob25lcyIpYCAqKk9ubGluZSBzdHVkZW50cyoqPC9zdW1tYXJ5Pg0KYHIgZW1vOjpqaSgic3BlZWNoX2JhbGxvb24iKWAgIFZvbHVudGVlciB0byBzaGFyZSB5b3VyIHNjcmVlbiB0byBzaG93IGFuZCBkZXNjcmliZSB5b3VyIHBsb3RseSBwbG90LiBIaWdobGlnaHQgYW55IGlzc3VlcyB5b3UgaGF2ZSBlbmNvdW50ZXJlZCB3aGlsZSBtYWtpbmcgdGhlIHBsb3QuIA0KPC9kZXRhaWxzPiANCg0KPGJyPg0KDQojIEV4dGVuc2lvbjogQWRkaW5nIEJ1dHRvbnMgdG8gYHBsb3RseWAgUGxvdHMgeyNidXR0b259DQoNCmByIGVtbzo6amkoImNvbXB1dGVyIilgIFNvIGZhciwgaG9wZWZ1bGx5IHRoZSBjb2RpbmcgaW4gdGhpcyBsYWIgaGFzIG5vdCBiZWVuIHRvbyBpbnRlbnNlLiBUaGF0J3MgYWJvdXQgdG8gY2hhbmdlLg0KDQpXaGlsZSBgc3VicGxvdHNgIGFyZSB1c2VmdWwgZm9yIGNvbWJpbmluZyBhIGNvdXBsZSBvZiBkaWZmZXJlbnQgcGxvdHMsIHRoZXkgY2FuIGJlY29tZSB1bndpZWxkeSB3aGVuIHdlIGNvbnNpZGVyIHRvbyBtYW55IHBsb3RzIGF0IG9uY2UuDQoNCkluIHRoaXMgZmluYWwgc2VjdGlvbiwgd2UnbGwgbG9vayBhdCBhbiBhbHRlcm5hdGl2ZSBhcHByb2FjaCAtIGBidXR0b25zYC4NCldlIGNhbiBhZGQgYGJ1dHRvbnNgIHRvIGEgYHBsb3RseWAgcGxvdCwgd2hpY2ggKHdoZW4gY2xpY2tlZCkgd2lsbCBhbGxvdyB1cyB0byBzaGlmdCBiZXR3ZWVuIGRpZmZlcmVudCBwcmVzZW50YXRpb25zIG9mIG91ciBkYXRhLiANCg0KKipQbGVhc2Ugbm90ZSAtIGJlZm9yZSB5b3UgYmVnaW4gdGhpcyBzZWN0aW9uLCBtYWtlIHN1cmUgeW91IGhhdmUgY29tcGxldGVkIHRoZSBbRGF0YSBTY2llbmNlIENvbXB1dGVyIExhYiAzXShodHRwczovL3JwdWJzLmNvbS9MVFVfU1RNMTAwMS9EU01DTDMpIGFuZCBoYXZlIHJlYWQgdGhyb3VnaCBbU2VjdGlvbiA1IG9mIHRoZSBEYXRhIFZpc3VhbGlzYXRpb24gaW4gUiBzdXBwbGVtZW50XShodHRwczovL2Jvb2tkb3duLm9yZy9yZWhrL3N0bTEwMDFfZHNtX2RhdGFfdmlzdWFsaXNhdGlvbl9pbl9yL2FkdmFuY2VkLXBsb3RseS1jb21wdXRlci1sYWItNGIuaHRtbCkuKioNCg0KKk5vdGU6IEl0IG1heSBhbHNvIGJlIGhlbHBmdWwgdG8gaGF2ZSB0aGVzZSBvcGVuIGluIGEgc2VwYXJhdGUgdGFiLCBzbyB0aGF0IHlvdSBjYW4gcmVmZXIgdG8gdGhlbSBhcyB5b3Ugd29yayB0aHJvdWdoIHRoaXMgc2VjdGlvbi4qDQoNCiMjDQoNCmByIGVtbzo6amkoImNvbXB1dGVyIilgIEl0IGlzIHdvcnRoIG5vdGluZyB0aGF0IGBwbG90bHlgIGdyYXBocyBpbmNvcnBvcmF0aW5nIGBidXR0b25zYCBjYW4gcnVuIGludG8gZGlmZmljdWx0aWVzIHdoZW4gdHJ5aW5nIHRvIHN3aXRjaCBiZXR3ZWVuIGRpZmZlcmVudCBkYXRhIHNldHMuDQoNClRvIGtlZXAgdGhpbmdzIGF0IGFuIGFwcHJvcHJpYXRlIGxldmVsIG9mIGRpZmZpY3VsdHksIHdlIHdpbGwgZm9jdXMgb24gcHJlc2VudGluZyBkYXRhIGZvciBvbmUgdmFyaWFibGUgZnJvbSB0aGUgYHBlbmd1aW5zYCBkYXRhIHNldCBhdCBhIHRpbWUgdXNpbmcgb3VyIGBidXR0b25zYC1lbmFibGVkIHBsb3RzLg0KDQojIyB7I2J1dHRvbnNwbG90fQ0KDQpgciBlbW86OmppKCJjb21wdXRlciIpYCBUbyBiZWdpbiwgd2Ugd2lsbCBjcmVhdGUgYW4gaW5pdGlhbCBzaW1wbGUgb2JqZWN0IHdpdGggYSBidXR0b24sIGFuZCB0aGVuIGJ1aWxkIHVwb24gdGhpcyB3aXRoIHN1YnNlcXVlbnQgYWRqdXN0bWVudHMuDQoNClRoZSBjb2RlIGNodW5rIGJlbG93IGNvbnRhaW5zIGFsbCB0aGUgY29kZSB3ZSBuZWVkIGZvciBvdXIgZmlyc3QgcGxvdC4NCg0KTm90ZSB0aGF0IGhlcmU6DQoNCiogV2Ugc3BlY2lmeSB0aGUgYGJ1dHRvbnNgIGluZm9ybWF0aW9uIHdpdGhpbiB0aGUgYGxheW91dGAgYXJndW1lbnRzLg0KKiBUaGUgYGJ1dHRvbnNgIGFyZ3VtZW50IGNhbiBjb250YWluIGEgYGxpc3RgIG9mIGluZm9ybWF0aW9uLCB3aXRoIGVhY2ggZWxlbWVudCBvZiB0aGUgYGxpc3RgIGNvcnJlc3BvbmRpbmcgdG8gYSBkaWZmZXJlbnQgZGF0YSB2aXN1YWxpc2F0aW9uLg0KKiBUbyBiZWdpbiwgd2UgaGF2ZSBqdXN0IHRoZSBvbmUgc3BlY2lmaWNhdGlvbiwgYSBoaXN0b2dyYW0gLSB0aGUgZGV0YWlscyBvZiB0aGUgaGlzdG9ncmFtIGFyZSBzcGVjaWZpZWQgd2l0aGluIGEgYGxpc3RgIG9iamVjdCAod2l0aGluIHRoZSBgYnV0dG9uc2AnIGBsaXN0YCkuDQoNClJ1biB0aGlzIGNvZGUgbm93LCBhbmQgdGhlbiBjYWxsIHRoZSBgcGVuZ3Vpbl9wbG90c2Agb2JqZWN0LCB0byBzZWUgdGhlIHJlc3VsdHMuIElnbm9yZSBhbnkgcmVkIHRleHQgdGhhdCBhcHBlYXJzLg0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IFQsIGVjaG8gPSBULCB3YXJuaW5nID0gRn0NCnBlbmd1aW5zX3Bsb3RzIDwtIHBsb3RfbHkoZGF0YSA9IHBlbmd1aW5zLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgeCA9IH5ib2R5X21hc3NfZywgDQogICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gfnNleCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9ycyA9ICJTZXQxIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIG9wYWNpdHkgPSAwLjYpICU+JSANCiAgbGF5b3V0KA0KICAgIA0KICAgIHRpdGxlID0gIlBlbmd1aW4ncyBCb2R5IE1hc3MgRGF0YSIsDQogICAgDQogICAgdXBkYXRlbWVudXMgPSBsaXN0KA0KICAgICAgDQogICAgICBsaXN0KHggPSAxLjIsIHkgPSAwLjcsIHR5cGUgPSAiYnV0dG9ucyIsIA0KICAgICAgICAgICANCiAgICAgICAgICMgV2UgcmVhbGx5IGp1c3QgaGF2ZSB0byBmb2N1cyBvbiB0aGUgY29kZSBiZWxvdyAgIA0KICAgICAgICAgYnV0dG9ucyA9IGxpc3QoDQogICAgICAgICAgIA0KICAgICAgICAgIGxpc3QobWV0aG9kID0gInJlc3R5bGUiLA0KICAgICAgICAgICAgICAgbGFiZWwgPSAiSGlzdG9ncmFtIiwgIyBUaGUgYnV0dG9uIGxhYmVsDQogICAgICAgICAgICAgICBhcmdzID0gbGlzdCgNCiAgICAgICAgICAgICAgICBsaXN0KHR5cGUgPSBsaXN0KCJoaXN0b2dyYW0iKSkpKSAjIFRoZSBwbG90IHR5cGUNCiAgICApKSkpDQpgYGANCg0KKk5vdGU6IFRoZSBzcGFjaW5nIGhlcmUgaXMgbm90IHN0cmljdGx5IG5lY2Vzc2FyeSwgYnV0IGhhcyBiZWVuIGNob3NlbiB3aXRoIHRoZSBhaW0gb2YgbWFraW5nIHRoZSBkaWZmZXJlbnQgYXJndW1lbnRzIGNsZWFyZXIuKg0KDQojIyB7I2FkZGJ1dHRvbn0NCg0KYHIgZW1vOjpqaSgiY29tcHV0ZXIiKWAgTm90ZSB0aGF0IGF0IHRoZSBtb21lbnQsIHRoZSAqSGlzdG9ncmFtKiBidXR0b24gaW4geW91ciBwbG90IHdvbid0IGRvIGFueXRoaW5nLCBhcyB3ZSBqdXN0IGhhdmUgdGhlIG9uZSBwbG90Lg0KDQpVc2luZyB0aGUgY29kZSBhYm92ZSBpbiBcQHJlZihidXR0b25zcGxvdCkgYXMgYSBndWlkZSwgYWRkIGNvZGUgdG8gdGhlIGBsYXlvdXRgIHNwZWNpZmljYXRpb25zIG9mIHRoZSBgcGVuZ3VpbnNfcGxvdHNgIG9iamVjdCBzbyB0aGF0LCBhcyBhIHNlY29uZCBvcHRpb24sIHRoZSBgcGVuZ3VpbnNgIGJvZHkgbWFzcyBkYXRhIGNhbiBhbHNvIGJlIHByZXNlbnRlZCBpbiBib3ggcGxvdHMuDQoNCipOb3RlOiBZb3Ugd2lsbCBuZWVkIHRvIGFkZCBhIGNvbW1hIGFmdGVyIGBsaXN0KHR5cGUgPSBsaXN0KCJoaXN0b2dyYW0iKSkpKWAgc2luY2UgeW91IGFyZSBhZGRpbmcgYW5vdGhlciBhcmd1bWVudCB0byB0aGUgYGJ1dHRvbnNgIGxpc3QuICoNCg0KKkhpbnQ6IElmIHlvdSBhcmUgc3R1Y2ssIGNoZWNrIHRoZSBjb2RlIGNodW5rIGJlbG93OioNCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSIsIGV2YWwgPSBGLCBlY2hvID0gVCwgd2FybmluZyA9IEZ9DQojIEFkZCBhIGNvbW1hIGF0IHRoZSBlbmQgb2YgdGhlIGV4aXN0aW5nIGxpbmUgb2YgY29kZQ0KbGlzdCh0eXBlID0gbGlzdCgiaGlzdG9ncmFtIikpKSkNCiMgYW5kIHRoZW4gcGFzdGUgdGhlIGZvbGxvd2luZyBvbiB0aGUgbmV4dCBsaW5lLCBiZWZvcmUgdGhlIGZpbmFsIGZvdXIgKSdzLg0KDQogICAgICAgICAgbGlzdChtZXRob2QgPSAicmVzdHlsZSIsDQogICAgICAgICAgICAgICBsYWJlbCA9ICJCb3ggUGxvdHMiLA0KICAgICAgICAgICAgICAgYXJncyA9IGxpc3QoDQogICAgICAgICAgICAgICAgbGlzdCh0eXBlID0gbGlzdCgiYm94IikpKSkNCiMgQ2hlY2sgd2l0aCB5b3VyIGxhYiBkZW1vbnN0cmF0b3IgaWYgdGhpcyBpcyB1bmNsZWFyLg0KYGBgDQoNCiMjIHsjdHdvYnV0dG9uc30NCg0KYHIgZW1vOjpqaSgiY29tcHV0ZXIiKWAgSWYgeW91ciBjb2RlIGhhcyB3b3JrZWQsIHlvdXIgcGxvdCBzaG91bGQgbm93IGhhdmUgdHdvIGJ1dHRvbnMsIHdoaWNoIHdlIGNhbiBzd2l0Y2ggYmV0d2VlbiAoYXMgc2hvd24gYmVsb3cpOg0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IFQsIGVjaG8gPSBGLCB3YXJuaW5nID0gRiwgbWVzc2FnZSA9IEZ9DQpwZW5ndWluc19wbG90cyA8LSBwbG90X2x5KGRhdGEgPSBwZW5ndWlucywgDQogICAgICAgICAgICAgICAgICAgICAgICAgIHggPSB+Ym9keV9tYXNzX2csIA0KICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IH5zZXgsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvcnMgPSAiU2V0MSIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICBvcGFjaXR5ID0gMC42KSAlPiUgDQogIGxheW91dCgNCiAgICANCiAgICB0aXRsZSA9ICJQZW5ndWluJ3MgQm9keSBNYXNzIERhdGEiLA0KICAgIA0KICAgIHVwZGF0ZW1lbnVzID0gbGlzdCgNCiAgICAgIA0KICAgICAgbGlzdCh4ID0gMS4yLCB5ID0gMC43LCB0eXBlID0gImJ1dHRvbnMiLCANCiAgICAgICAgICAgDQogICAgICAgICAjIFdlIHJlYWxseSBqdXN0IGhhdmUgdG8gZm9jdXMgb24gdGhlIGNvZGUgYmVsb3cgICANCiAgICAgICAgIGJ1dHRvbnMgPSBsaXN0KA0KICAgICAgICAgICANCiAgICAgICAgICBsaXN0KG1ldGhvZCA9ICJyZXN0eWxlIiwNCiAgICAgICAgICAgICAgIGxhYmVsID0gIkhpc3RvZ3JhbSIsICMgVGhlIGJ1dHRvbiBsYWJlbA0KICAgICAgICAgICAgICAgYXJncyA9IGxpc3QoDQogICAgICAgICAgICAgICAgbGlzdCh0eXBlID0gbGlzdCgiaGlzdG9ncmFtIikpKSksICMgVGhlIHBsb3QgdHlwZQ0KICAgICAgICAgIA0KICAgICAgICAgICAgbGlzdChtZXRob2QgPSAicmVzdHlsZSIsDQogICAgICAgICAgICAgICBsYWJlbCA9ICJCb3ggUGxvdHMiLA0KICAgICAgICAgICAgICAgYXJncyA9IGxpc3QoDQogICAgICAgICAgICAgICAgbGlzdCh0eXBlID0gbGlzdCgiYm94IikpKSkNCiAgICApKSkpDQpwZW5ndWluc19wbG90cw0KYGBgDQoNCg0KIyMNCg0KYHIgZW1vOjpqaSgiY29tcHV0ZXIiKWAgR29vZCB3b3JrISBMZXQncyB0YWtlIGEgc3RlcCBiYWNrLCBhbmQgY29uc2lkZXIgc29tZSBzbWFsbGVyIG1vZGlmaWNhdGlvbnMgd2UgY2FuIG1ha2UgdG8gb3VyIGNvZGUgaW4gXEByZWYodHdvYnV0dG9ucykuDQoNClRvIHN0YXJ0LCBwZXJoYXBzIGluc3RlYWQgb2YgYm90aCBidXR0b25zIHNob3dpbmcsIHdlIHdvdWxkIGxpa2UgYSBkcm9wZG93biBtZW51LiBUaGlzIGNhbiBiZSBhY2hpZXZlZCBieSBjaGFuZ2luZyB0aGUgYHR5cGUgPSAiYnV0dG9ucyJgIGNvZGUgaW4gb3VyIGBwZW5ndWluc19wbG90c2Agb2JqZWN0IGNvZGUgdG8gYHR5cGUgPSAiZHJvcGRvd24iYCAod2hpY2ggbWFrZXMgc2Vuc2UpLiANCg0KVHJ5IG1ha2luZyB0aGlzIGNoYW5nZSBub3csIGFuZCBjaGVjayB0aGUgcmVzdWx0cy4NCg0KKk5vdGU6IERlc3BpdGUgY2hhbmdpbmcgdGhlIGB0eXBlID0gImJ1dHRvbnMiYCBjb2RlLCB3ZSBrZWVwIHRoZSBgYnV0dG9ucyA9IGxpc3QoLi4uKWAgc3BlY2lmaWNhdGlvbiwgdGhpcyBkb2Vzbid0IG5lZWQgdG8gY2hhbmdlLioNCg0KIyMNCg0KYHIgZW1vOjpqaSgiY29tcHV0ZXIiKWAgWW91IHdpbGwgaGF2ZSBub3RpY2VkIHRoYXQgdGhlIGBwZW5ndWluc19wbG90c2AncyBoaXN0b2dyYW0gbG9va3MgYSBsaXR0bGUgZGlmZmVyZW50IHRvIHRoZSBvbmUgaW4gb3VyIGNvbWJpbmVkIGBzdWJwbG90YCBmcm9tIFxAcmVmKGNvbWJpbmVkKS4gVGhpcyBpcyBiZWNhdXNlIHdlIGhhdmUgbm90IHNwZWNpZmllZCBoZXJlIHRoYXQgdGhlIGhpc3RvZ3JhbXMgc2hvdWxkIG92ZXJsYXkgZWFjaCBvdGhlci4gUmVjYWxsIHRoYXQgd2UgY2FuIGRvIHRoaXMgdmlhIHRoZSBgYmFybW9kZSA9Im92ZXJsYXkiYCBjb21tYW5kLg0KDQpOb3JtYWxseSwgd2Ugd291bGQgaGF2ZSB0byBzcGVjaWZ5IHRoaXMgd2hlbiBjcmVhdGluZyBvdXIgb3JpZ2luYWwgcGxvdC4gSG93ZXZlciwgYnkgdXNpbmcgdGhlIHBpcGUgb3BlcmF0b3IgYW5kIHRoZSBgbGF5b3V0YCBmdW5jdGlvbiwgd2UgY2FuIGVhc2lseSBhZGQgdGhpcyBpbiB0byBvdXIgYHBlbmd1aW5zX3Bsb3RzYCBwbG90LiANCg0KVHJ5IGluc2VydGluZyB0aGUgYGJhcm1vZGUgPSJvdmVybGF5ImAgY29tbWFuZCBpbnRvIHlvdXIgY29kZSBmb3Igb3VyIG5ldyBgcGVuZ3VpbnNfcGxvdHNgIG9iamVjdCBub3cuDQoNCipIaW50OiBJdCBkb2Vzbid0IG5lZWQgdG8gYmUgYWRkZWQgd2l0aGluIHRoZSBgdXBkYXRlbWVudXNgIGZ1bmN0aW9uLioNCg0KIyMNCg0KYHIgZW1vOjpqaSgiY29tcHV0ZXIiKWAgTmV4dCwgdG8gcmVhbGx5IGFwcHJlY2lhdGUgdGhlIGJlbmVmaXQgb2YgdGhlIGBidXR0b25zYCBhcHByb2FjaCBvdmVyIHRoZSBgc3VicGxvdGAgYXBwcm9hY2gsIGxldCdzIGFkZCBhIHRoaXJkIHBsb3QgaW4gb3VyIFxAcmVmKGJ1dHRvbnNwbG90KSBjb2RlLg0KDQpVc2luZyB5b3VyIGNvZGUgZnJvbSBcQHJlZihhZGRidXR0b24pIGFzIGEgZ3VpZGUsIGFkZCBjb2RlIHRvIHRoZSBgbGF5b3V0YCBzcGVjaWZpY2F0aW9ucyBvZiB0aGUgYHBlbmd1aW5zX3Bsb3RzYCBvYmplY3Qgc28gdGhhdCB0aGUgYHBlbmd1aW5zYCBib2R5IG1hc3MgZGF0YSBjYW4sIGFzIGEgdGhpcmQgb3B0aW9uLCBhbHNvIGJlIHByZXNlbnRlZCBpbiB2aW9saW4gcGxvdHMuDQoNCipOb3RlOiBUbyBlbnN1cmUgdGhlIGJveCBwbG90cyBhcmUgc2hvd24gd2l0aGluIHRoZSB2aW9saW4gcGxvdHMsIHlvdSB3aWxsIG5lZWQgdG8gYWxzbyBhZGQgdGhlIGNvZGUgYGJveCA9IGxpc3QodmlzaWJsZSA9IFQgKWAgaW4geW91ciB2aW9saW4gcGxvdCBzcGVjaWZpY2F0aW9ucy4qDQoNCiMjDQoNCmByIGVtbzo6amkoImNvbXB1dGVyIilgIE5vdyB0aGF0IHlvdSBmZWVsIG1vcmUgY29tZm9ydGFibGUgdXNpbmcgYGJ1dHRvbnNgLCB0cnkgdG8gY29tcGxldGUgdGhlIGZvbGxvd2luZyBzdGVwczoNCg0KKiBTd2l0Y2ggdGhlIG9yZGVyIG9mIGBidXR0b25zYCBhcm91bmQgc28gdGhhdCB2aW9saW4gcGxvdHMgYXJlIHNob3duIGZpcnN0IGFuZCB0aGUgaGlzdG9ncmFtIGlzIHNob3duIGxhc3QuDQoqIENoYW5nZSB0aGUgZGF0YSBzaG93biBmcm9tIGJlaW5nIGJvZHkgbWFzcyBkYXRhLCB0byBiaWxsIGxlbmd0aCBkYXRhLCBhbmQgYWRqdXN0IHRoZSB0aXRsZSBhY2NvcmRpbmdseS4NCiogQ29sb3VyIHRoZSBvYnNlcnZhdGlvbnMgYnkgYHNwZWNpZXNgLCBub3QgYHNleGAuDQoqIEFkZCBhIHJhbmdlc2xpZGVyIHRvIHlvdXIgcGxvdHMuDQoNCiMjIA0KDQpgciBlbW86OmppKCJjb21wdXRlciIpYCBBcyB5b3UgY2FuIHNlZSwgd2hlbiB3ZSBhcmUgZGVhbGluZyB3aXRoIHBsb3RzIHByZXNlbnRpbmcgbXVsdGlwbGUgdmFyaWFibGVzLCBpdCBtYXkgYmUgYmV0dGVyIHRvIHVzZSBgc3VicGxvdHNgLCB3aGlsZSBpZiB3ZSBhcmUgZGVhbGluZyB3aXRoIGRpZmZlcmVudCBwbG90cyBvZiB0aGUgb25lIHZhcmlhYmxlLCBpdCBtYXkgYmUgYmV0dGVyIHRvIHVzZSBgYnV0dG9uc2AuIFBlcmhhcHMgYSBtaXh0dXJlIGlzIGJlc3QuDQoNCkRvIHlvdSBoYXZlIGEgcHJlZmVyZW5jZSBmb3IgYHN1YnBsb3RzYCBvciBgYnV0dG9uc2A/DQoNCjxicj4NCg0KPGRldGFpbHM+DQogIDxzdW1tYXJ5PmByIGVtbzo6amkoImhlYWRwaG9uZXMiKWAgKipPbmxpbmUgc3R1ZGVudHMqKjwvc3VtbWFyeT4NCmByIGVtbzo6amkoInNwZWVjaF9iYWxsb29uIilgICBWb2x1bnRlZXIgdG8gc2hhcmUgeW91ciBzY3JlZW4gdG8gc2hvdyBhbmQgZGVzY3JpYmUgeW91ciBwbG90bHkgcGxvdC4gSGlnaGxpZ2h0IGFueSBpc3N1ZXMgeW91IGhhdmUgZW5jb3VudGVyZWQgd2hpbGUgbWFraW5nIHRoZSBwbG90LiANCjwvZGV0YWlscz4gDQoNCjxicj4NCg0KPGJyPg0KDQojIyMjIFdlbGwgZG9uZSEgVGhlcmUgd2FzIGEgbG90IG9mIGNvbnRlbnQgdG9kYXkuIFlvdSBoYXZlIGNvbWUgYSBsb25nIHdheSBmcm9tIHRoYXQgZmlyc3QgYmFzZSBSIGhpc3RvZ3JhbSB5b3UgY3JlYXRlZCBiYWNrIGluIHRoZSBbc2Vjb25kIERhdGEgU2NpZW5jZSBDb21wdXRlciBMYWJdKGh0dHBzOi8vcnB1YnMuY29tL0xUVV9TVE0xMDAxL0RTTUNMMikuICMjIyMgey19DQoNCkRvbid0IHdvcnJ5IGlmIHlvdSB3ZXJlbid0IGFibGUgdG8gZmluaXNoIGV2ZXJ5dGhpbmcgaW4gdGhlIG9uZSBzZXNzaW9uIC0gdGhlcmUgaXMgcXVpdGUgb2YgbG90IG9mIG1hdGVyaWFsIHRvIHdvcmsgdGhyb3VnaCBpbiB0aGlzIGxhYiwgYW5kIGl0J3Mgbm90IGVhc3kuDQoNCkhvcGVmdWxseSB0aG91Z2gsIHlvdSBhcmUgYmVnaW5uaW5nIHRvIGZlZWwgcXVpdGUgc2tpbGxlZCB3aXRoIHVzaW5nIGBwbG90bHlgLiBUaGUgdGVjaG5pcXVlcyBhbmQgY29kaW5nIHNraWxscyB5b3UgYXJlIGxlYXJuaW5nIHNob3VsZCBob2xkIHlvdSBpbiBnb29kIHN0ZWFkIGZvciB0aGUgZm9sbG93aW5nIHdlZWtzLiBSZW1lbWJlciwgeW91IGNhbiBhbHdheXMgcmVmZXIgYmFjayB0byB0aGlzIG1hdGVyaWFsIGF0IGEgbGF0ZXIgZGF0ZSBpZiB5b3UgbmVlZCBhIHF1aWNrIHJlZnJlc2hlci4NCg0KQmVmb3JlIHlvdSBmaW5pc2ggdXAsIG1ha2Ugc3VyZSB0byBzYXZlIHlvdXIgc2NyaXB0IGZpbGUgc29tZXdoZXJlIHNhZmUgLSBpdCBtaWdodCBjb21lIGluIGhhbmR5IGxhdGVyIG9uLiANCg0KPGJyPg0KDQojIFJlZmVyZW5jZXMgey0gI1JlZn0NCjxkaXYgaWQ9InJlZnMiPjwvZGl2Pg0KDQo8YnI+DQoNCjxmb250IGNvbG9yID0gImdyZXkiPg0KVGhlc2Ugbm90ZXMgaGF2ZSBiZWVuIHByZXBhcmVkIGJ5IFJ1cGVydCBLdXZla2UuIFRoZSBjb3B5cmlnaHQgZm9yIHRoZSBtYXRlcmlhbCBpbiB0aGVzZSBub3RlcyByZXNpZGVzIHdpdGggdGhlIGF1dGhvciBuYW1lZCBhYm92ZSwgd2l0aCB0aGUgRGVwYXJ0bWVudCBvZiBNYXRoZW1hdGljYWwgYW5kIFBoeXNpY2FsIFNjaWVuY2VzIGFuZCB3aXRoIExhIFRyb2JlIFVuaXZlcnNpdHkuIENvcHlyaWdodCBpbiB0aGlzIHdvcmsgaXMgdmVzdGVkIGluIExhIFRyb2JlIFVuaXZlcnNpdHkgaW5jbHVkaW5nIGFsbCBMYSBUcm9iZSBVbml2ZXJzaXR5IGJyYW5kaW5nIGFuZCBuYW1pbmcuIFVubGVzcyBvdGhlcndpc2Ugc3RhdGVkLCBtYXRlcmlhbCB3aXRoaW4gdGhpcyB3b3JrIGlzIGxpY2Vuc2VkIHVuZGVyIGEgQ3JlYXRpdmUgQ29tbW9ucyBBdHRyaWJ1dGlvbi1Ob24gQ29tbWVyY2lhbC1Ob24gRGVyaXZhdGl2ZXMgTGljZW5zZSANCjxhIGhyZWYgPSAiaHR0cHM6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL2xpY2Vuc2VzL2J5LW5jLW5kLzQuMC9DQyIgdGFyZ2V0PSJfYmxhbmsiPiBCWS1OQy1ORC4gPC9hPg0KPC9mb250Pg==