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.


1 Preparation

🏡 Before we begin our work, we will need to carry out some initial preparations.

1.1

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.

1.2

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.

2 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.

2.1

💻 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.

2.2

💻 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!

2.3

💻 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.


3 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!

3.1

💻 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.

3.2

💻 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.

3.3

💻 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.


4 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.

4.1

💻 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).

4.2

💻 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.

4.3

💻 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.

4.4

💻 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.


5 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.

5.1

💻 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.

5.2

💻 To begin, we will create an initial simple object with a button, and then build upon this with subsequent adjustments.

The code chunk below contains all the code we need for our first plot.

Note that here:

  • We specify the buttons information within the layout arguments.
  • The buttons argument can contain a list of information, with each element of the list corresponding to a different data visualisation.
  • To begin, we have just the one specification, a histogram - the details of the histogram are specified within a list object (within the buttonslist).

Run this code now, and then call the penguin_plots object, to see the results. Ignore any red text that appears.

penguins_plots <- plot_ly(data = penguins, 
                          x = ~body_mass_g, 
                          color = ~sex, 
                          colors = "Set1", 
                          opacity = 0.6) %>% 
  layout(
    
    title = "Penguin's Body Mass Data",
    
    updatemenus = list(
      
      list(x = 1.2, y = 0.7, type = "buttons", 
           
         # We really just have to focus on the code below   
         buttons = list(
           
          list(method = "restyle",
               label = "Histogram", # The button label
               args = list(
                list(type = list("histogram")))) # The plot type
    ))))

Note: The spacing here is not strictly necessary, but has been chosen with the aim of making the different arguments clearer.

5.3

💻 Note that at the moment, the Histogram button in your plot won’t do anything, as we just have the one plot.

Using the code above in 5.2 as a guide, add code to the layout specifications of the penguins_plots object so that, as a second option, the penguins body mass data can also be presented in box plots.

Note: You will need to add a comma after list(type = list("histogram")))) since you are adding another argument to the buttons list.

Hint: If you are stuck, check the code chunk below:

# Add a comma at the end of the existing line of code
list(type = list("histogram"))))
# and then paste the following on the next line, before the final four )'s.

          list(method = "restyle",
               label = "Box Plots",
               args = list(
                list(type = list("box"))))
# Check with your lab demonstrator if this is unclear.

5.4

💻 If your code has worked, your plot should now have two buttons, which we can switch between (as shown below):

5.5

💻 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.

5.6

💻 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.

5.7

💻 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.

5.8

💻 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.

5.9

💻 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==