Data Science Module
Topic 4B: Data Visualisation IV
Welcome to the fourth Data Science computer lab.
In this computer lab we will extend our skills in creating interactive R content and, using the Shiny R package (Chang et al. 2021), we will create a web app, all within R.
This computer lab is designed to run alongside the content on R functions in Section 4.2 of the Data Visualisation in R supplement and the content on Shiny apps in Section 6 of the Data Visualisation in R supplement. Make sure that you read this content before you begin the lab questions. It may also be helpful to keep this content open in separate tabs while you work through the lab material.
By the end of this lab, you should be able to create a simple web app using Shiny. Let’s get started!
Preparations
Before we go any further, please make sure you have read the content in Section 6 of the Data Visualisation in R supplement - if you haven’t, these lab questions will not make sense.
If you have not already installed Shiny, please do so now, and load Shiny in your current R session.
If you need a refresher, you can check the Code
chunk below.
install.packages("shiny")
library(shiny)
Ok, let’s get started on creating our first Shiny app.
To begin, create a new folder on your computer called shiny_app_test
, and then set this folder as your current working directory in R.
Hint: To check your current working directory, run the command getwd()
. Then just make sure you have created a new directory for your app within your current working directory.
Copy all the R code in the Code
chunk below into a new R script file, and save this script as app.R
within your new folder shiny_app_test
.
library(shiny)
# Define UI for app that draws a histogram ----
ui <- fluidPage(
# App title ----
titlePanel("Hello Shiny!"),
# Sidebar layout with input and output definitions ----
sidebarLayout(
# Sidebar panel for inputs ----
sidebarPanel(
# Input: Slider for the number of bins ----
sliderInput(inputId = "bins",
label = "Number of bins:",
min = 1,
max = 50,
value = 30)
),
# Main panel for displaying outputs ----
mainPanel(
# Output: Histogram ----
plotOutput(outputId = "distPlot")
)
)
)
# Define server logic required to draw a histogram ----
server <- function(input, output) {
# Histogram of the Old Faithful Geyser Data ----
# with requested number of bins
# This expression that generates a histogram is wrapped in a call
# to renderPlot to indicate that:
#
# 1. It is "reactive" and therefore should be automatically
# re-executed when inputs (input$bins) change
# 2. Its output type is a plot
output$distPlot <- renderPlot({
x <- faithful$waiting
bins <- seq(min(x), max(x), length.out = input$bins + 1)
hist(x, breaks = bins, col = "#75AADB", border = "white",
xlab = "Waiting time to next eruption (in mins)",
main = "Histogram of waiting times")
})
}
shinyApp(ui = ui, server = server)
To check that everything is working, open a separate script file, and run the command runApp("app.R")
.
The Hello Shiny!
example Shiny app discussed in Section 6 of the Data Visualisation in R supplement should appear.
For the moment, close the app - it’s time to make some changes.
Note: If the app does not appear in a new window, and you receive an error message, try double-checking the steps outlined above, checking that your app.R
script contains only the code provided in the 1.3 code chunk, and checking that you have the shiny
R package installed.
Modifying an existing Shiny app
Now that we have a working app.R
script, let’s begin to tinker with the code.
Open up your app.R
script, and change the title of your app from “Hello Shiny!” to “Hello Data Science”.
Next, change the settings on the slider, so the minimum is 3, the maximum is 40, and the starting number is 20.
Next, see if you can change the colour of the histogram to another colour.
Recall that within our app.R
script, the histogram produced in the server
function is assigned to the output distPlot
via output$distPlot
. In the ui
object within our app.R
script, distPlot
is then called as the output to be presented in the web app, via the plotOutput(outputId = "distPlot")
function.
In other words, output$distPlot
matches plotOutput(outputId = "distPlot")
.
Try changing the histogram output name from distPlot
to displayHist
.
Hint: You will have to make this change in two places.
When you are happy with all these changes, relaunch your app to verify that your changes have worked as desired.
Making a Shiny app for the Penguins data
Now that we have familiarised ourselves with the code structure of a Shiny app, let’s make a Shiny app that displays penguins
data from the palmerpenguins
R package (Horst, Hill, and Gorman 2020).
By now you should be quite familiar with this data, so we won’t need to spend any time exploring the characteristics of the penguins
data set.
Let’s begin.
Following the process outlined in Section 6 of the Data Visualisation in R supplement, and using the code in 1.3, produce a simple Shiny web app that displays a histogram of the flipper length (in mm) of the penguins in the Palmer Archipelago. This app should include a slider so that users can select the number of bins displayed in the histogram.
As a guide, try to follow these steps:
- Create a new directory for your penguins app (remember each app should have a separate directory).
- Create your
app.R
script file and save it in your new directory (use the code in 1.3 for the base version of this script).
- Modify this script as required. Don’t worry about changing the bin specifications - just focus on the data being used.
- Change the title of the app, and the labels on the histogram.
Note that you will need to make sure that you load the palmerpenguins
package within your app.R
file.
One aspect of the penguins
data set which we have brushed over in previous labs is that it contains several missing values. These can cause difficulties with our code. To avoid this, please include the following line of code, directly after the line in which you load the palmerpenguins
package:
penguins <- na.omit(penguins)
# this removes the NA observations in the penguins data set
Once you are happy with your app, run it.
If all goes well, you should end up with an app that looks something like this:
Note: If you have made a decent effort to create this app, but just can’t seem to get it fully working, take a look at the code in the Code
chunk below:
library(shiny)
library(palmerpenguins)
penguins <- na.omit(penguins)
# Define UI for app that draws a histogram ----
ui <- fluidPage(
# App title ----
titlePanel("Palmer Penguins Flipper Length"),
# Sidebar layout with input and output definitions ----
sidebarLayout(
# Sidebar panel for inputs ----
sidebarPanel(
# Input: Slider for the number of bins ----
sliderInput(inputId = "bins",
label = "Number of bins:",
min = 1,
max = 50,
value = 30)
),
# Main panel for displaying outputs ----
mainPanel(
# Output: Histogram ----
plotOutput(outputId = "flipperPlot")
)
)
)
# Define server logic required to draw a histogram ----
server <- function(input, output) {
output$flipperPlot <- renderPlot({
x <- penguins$flipper_length_mm
bins <- seq(min(x), max(x), length.out = input$bins + 1)
hist(x, breaks = bins, col = "#75AADB", border = "white",
xlab = "Flipper length of penguins (mm)",
main = "Histogram of Penguin Flipper Lengths")
})
}
shinyApp(ui = ui, server = server)
Adding widgets to a Shiny app
A widget is an interactive web element. Widgets have a default value, specified in our app.R
script, and users can change this value by interacting with the widget. In fact, the current version of our penguin Shiny app already features a widget - the histogram bin slider!
We can add multiple widgets to our Shiny app, to enhance user experience. Let’s try this now.
Shiny contains a family of pre-built widgets, each with their own R function. For our slider bar, we used the widget function sliderInput
. Some other options - and their corresponding R functions - include:
- Checkbox (single) -
checkboxInput
- Checkbox (group) -
checkboxGroupInput
- Numeric Input -
numericInput
- Text Input -
textInput
- Date Range -
dateRangeInput
- Select Box -
selectInput
As you can see, these function names are fairly intuitive, and all contain the ending Input
.
For our app.R
script, we will focus on adding a Select Box (using selectInput
), which is a box with choices from which to select.
We can add widgets to our app.R
script by placing the relevant code within one of the Panel
sections of our ui
object (either the sidebarPanel()
or mainPanel()
sections).
Each widget function takes a set of specific arguments (which is why we are only focusing on adding one right now!). The first two arguments for all widgets are however the same:
- The widget must have a name
- The widget must have a label
Let’s take another look at the code for the slider bar widget for our app.
sliderInput(inputId = "bins",
label = "Number of bins:",
min = 1,
max = 50,
value = 30),
You can see here that the first argument, inputId = "bins"
, is telling R that the name of this widget is "bins"
- this is used to access the value of the widget. This value is defined in the server
function, via the line of code bins <- seq(min(x), max(x), length.out = input$bins + 1)
.
The second argument, label = "Number of bins:"
specifies the widget label that will appear in your app.
Note: You can refer to Figure 3.1 to check this label.
Let’s keep things simple, and try to add a scatter plot to our app. For this scatter plot, we would like to use a Select Box to select the penguins
variable plotted on the vertical axis. For the horizontal axis, we will use the variable body_mass_g
.
To begin, we need to add a selectInput
widget to the ui
object section of your app.R
script. This widget should allow users to select between the variables bill_length_mm
, bill_depth_mm
and `flipper_length_mm
.
Use the code below as a guide to do this now. Once completed, place this chunk of code within the sidebarPanel()
function, before the sliderInput
function.
Hint: You will have to replace the ...
s with appropriate code.
selectInput(inputId = "select",
label = "Penguin Variables:",
choices = c("Bill Length (mm)" = "bill_length_mm",
...)),
Note that here:
- The input reference ID is
select
(equivalent to bins
for the histogram bin slider)
- The label that will appear above the widget is “Penguin Variables:”
Once you are happy with your code, try re-running your app.
If all goes well, you should end up with a new widget in your app, that when clicked looks something like this:
Making Reactive Outputs for Shiny
Hang on a minute though…we’ve added a widget but nothing’s happening!
This is because we haven’t yet coded our app to have reactive output, based on what the user selects from our Select Box widget. Let’s rectify that now.
Recall that we use the plotOutput(outputId = "distPlot"),
command within the mainPanel
section of our ui
object to tell our app to display the object distPlot
.
If we want our new widget to change the variables plotted in our scatter plot, we must tell R that we want a scatter plot to be displayed!
Let’s call this scatter plot object scatterPlot
.
Using the code command shown earlier in 5.1 for distPlot
, add a line of code in the mainPanel
section to tell R to display the object scatterPlot
.
Hint: You can check the Code
chunk below if you need to double-check your work.
plotOutput(outputId = "scatterPlot"),
Great! Now all we need to do is include some code in the server
function section of our app.R
script, to actually carry out the calculations to plot the scatter plot.
We will keep this simple. Read over the code below, and then add it inside your server
function.
output$scatterPlot <- renderPlot({
w <- penguins[, c("body_mass_g", input$select), drop = FALSE]
plot(w, col = "#75AADB")
})
Note that just as we call the user input value for the histogram bin slider via input$bin
, we can call the user input value for our scatter plot select box here via input$select
(take a look back at 4.3 if this doesn’t make sense).
We should now be good to go. Try re-running your app. If all goes according to plan, you should now have a reactive scatter plot included in your app!
Modifying the Shiny User Interface
Excellent! We now have a responsive scatter plot displaying the relationship between the Palmer Penguins’ body masses and other measurements. However, our user interface is looking a little basic - let’s add some details to finish up.
There are many Shiny user interface options available to us, and we won’t have time to cover all them in this lab, so please keep in mind that the following options are just a selection from a wider set.
The fluidPage
function within the ui
object of our app.R
script is used to create a dynamic display for our Shiny app - i.e. it will adjust to the dimensions of your user’s screen.
By placing different elements within this function, we can change the appearance and layout of our app.
Recall that we have three panels in a Shiny app (the title panel, the side panel, and the main panel).
Currently, our Penguin app has a sidebar on the left-hand side. Try changing this now, by including the element position = "right"
inside the sidebarLayout()
function.
Try switching the positions of the widgets in the sidebar, by swapping their code chunks in the ui
object (you should be able to select the whole chunk corresponding to the widget, cut it and then paste it above/below the code chunk for the other widget)
Note: Make sure you have a comma only at the end of the first widget code chunk.
Formatted text
We can add text to our different panels, to provide details about our data.
To add helpful text above or below a widget (to describe the purpose of the widget), we can use the helpText
function, within the sidebarPanel
function. To display the text above the widget, put the helpText
code above the widget. To display the text below the widget, put the helpText
code below the widget - simple as that.
Take a look at the code below for an example.
helpText("this is some helpful text to describe a widget"),
If we would like to add formatted text, such as a paragraph, we can use the p
function, within the relevant panel.
Take a look at the code below for an example.
p("using p creates a paragraph of text."),
Finally, we can also add images to our app.
This is relatively simple, but there is one step we must perform first.
Any image file we add must be in a folder named www
within the directory containing your app.R
script.
- Make this folder now.
- Search the Creative Commons image database for a penguin image.
- Save it with the file name
penguin.jpg
in your www
folder.
To insert the image, we can use the code img(src = "penguin.jpg")
in a suitable place within our app.R
script - for instance within the mainPanel
section.
The image size might be too large or too small - we can modify the height and width dimensions (in pixels) using the additional commands height
and width
(which seems logical) in the img()
function.
Take a look at the code below for an example.
img(src = "penguin.jpg", height = 300),
Using this information, try to make your image fit more nicely within your app by changing its display dimensions.
Challenge
To test your understanding of the information presented above, use the various functions and elements we have just covered to try and recreate the Shiny app pictured below.
Don’t worry if you can’t get all the elements to work (and of course your penguin image may be different).
A copy of the app.R
script used to generate the app shown below will be included in the solutions to this computer lab, so you can check that if there were any parts which you were unsure about.
Publishing your Shiny App
For details on how to publish your Shiny app online, please refer to Section 6 of the Data Visualisation in R supplement.
Great job, that’s everything for today!
That was a lot to cover, but hopefully this lab has inspired you to learn more Shiny skills in the future.
Do you have a preference for Plotly or Shiny?
Remember, you can always refer back to this material at a later date if you need a quick refresher.
References
Chang, Winston, Joe Cheng, JJ Allaire, and Carson Sievert et al. 2021.
shiny: Web Application Framework for R.
https://shiny.rstudio.com/.
Horst, Allison Marie, Alison Presmanes Hill, and Kristen B Gorman. 2020.
Palmerpenguins: Palmer Archipelago (Antarctica) Penguin Data.
https://doi.org/10.5281/zenodo.3960218.
RStudio Team. 2020.
RStudio: Integrated Development Environment for R. Boston, MA: RStudio, PBC.
http://www.rstudio.com/.
These notes have been prepared by Rupert Kuveke. Please note that some of the content in these notes has been developed from the R Studio Shiny Tutorial (RStudio Team 2020). The copyright for the material in these notes resides with the authors 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.
LS0tDQp0aXRsZTogIlNUTTEwMDE6IENvbXB1dGVyIExhYiA0QiINCm91dHB1dDoNCiAgYm9va2Rvd246Omh0bWxfZG9jdW1lbnQyOiANCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZmxvYXQ6IHRydWUNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgdGhlbWU6IHJlYWRhYmxlDQogICAgY29kZV9mb2xkaW5nOiBzaG93DQpiaWJsaW9ncmFwaHk6IFNUTTEwMDFfRFNfQ0xfcmVmZXJlbmNlcy5iaWIgDQpsaW5rLWNpdGF0aW9uczogeWVzDQotLS0NCg0KPHN0eWxlPg0KI1RPQyB7DQogIGJhY2tncm91bmQ6IHVybCgiaHR0cHM6Ly93d3cubGF0cm9iZS5lZHUuYXUvX21lZGlhL2xhLXRyb2JlLWFwaS92NS9pbWcvbG9nby5zdmciKTsNCiAgYmFja2dyb3VuZC1zaXplOiBjb250YWluOw0KICBwYWRkaW5nLXRvcDogODBweCAhaW1wb3J0YW50Ow0KICBiYWNrZ3JvdW5kLXJlcGVhdDogbm8tcmVwZWF0Ow0KfQ0KPC9zdHlsZT4NCg0KIyMjIERhdGEgU2NpZW5jZSBNb2R1bGUgey19DQoNCiMjIyBUb3BpYyA0QjogRGF0YSBWaXN1YWxpc2F0aW9uIElWIHstfQ0KDQoNCjxicj4NCg0KV2VsY29tZSB0byB0aGUgZm91cnRoIERhdGEgU2NpZW5jZSBjb21wdXRlciBsYWIuDQpJbiB0aGlzIGNvbXB1dGVyIGxhYiB3ZSB3aWxsIGV4dGVuZCBvdXIgc2tpbGxzIGluIGNyZWF0aW5nIGludGVyYWN0aXZlIFIgY29udGVudCBhbmQsIHVzaW5nIHRoZSBTaGlueSBSIHBhY2thZ2UgW0BzaGlueV0sIHdlIHdpbGwgY3JlYXRlIGEgd2ViIGFwcCwgYWxsIHdpdGhpbiBSLg0KDQpUaGlzIGNvbXB1dGVyIGxhYiBpcyBkZXNpZ25lZCB0byBydW4gYWxvbmdzaWRlIHRoZSBjb250ZW50IG9uIFIgZnVuY3Rpb25zIGluIFtTZWN0aW9uIDQuMiBvZiB0aGUgRGF0YSBWaXN1YWxpc2F0aW9uIGluIFIgc3VwcGxlbWVudF0oaHR0cHM6Ly9ib29rZG93bi5vcmcvcmVoay9zdG0xMDAxX2RzbV90MV9kYXRhX3Zpc3VhbGlzYXRpb25faW5fci9yLWNvZGluZy10ZWNobmlxdWVzLmh0bWwjd3JpdGluZy1yLWZ1bmN0aW9ucy1jb21wdXRlci1sYWItNGIpIGFuZCB0aGUgY29udGVudCBvbiBTaGlueSBhcHBzIGluIFtTZWN0aW9uIDYgb2YgdGhlIERhdGEgVmlzdWFsaXNhdGlvbiBpbiBSIHN1cHBsZW1lbnRdKGh0dHBzOi8vYm9va2Rvd24ub3JnL3JlaGsvc3RtMTAwMV9kc21fdDFfZGF0YV92aXN1YWxpc2F0aW9uX2luX3IvY3JlYXRpbmctd2ViLWFwcHMtdXNpbmctc2hpbnktY29tcHV0ZXItbGFiLTRiLmh0bWwpLiAqKk1ha2Ugc3VyZSB0aGF0IHlvdSByZWFkIHRoaXMgY29udGVudCBiZWZvcmUgeW91IGJlZ2luIHRoZSBsYWIgcXVlc3Rpb25zLiBJdCBtYXkgYWxzbyBiZSBoZWxwZnVsIHRvIGtlZXAgdGhpcyBjb250ZW50IG9wZW4gaW4gc2VwYXJhdGUgdGFicyB3aGlsZSB5b3Ugd29yayB0aHJvdWdoIHRoZSBsYWIgbWF0ZXJpYWwuKioNCg0KQnkgdGhlIGVuZCBvZiB0aGlzIGxhYiwgeW91IHNob3VsZCBiZSBhYmxlIHRvIGNyZWF0ZSBhIHNpbXBsZSB3ZWIgYXBwIHVzaW5nIFNoaW55LiBMZXQncyBnZXQgc3RhcnRlZCENCg0KIyBQcmVwYXJhdGlvbnMNCg0KQmVmb3JlIHdlIGdvIGFueSBmdXJ0aGVyLCBwbGVhc2UgbWFrZSBzdXJlIHlvdSBoYXZlIHJlYWQgdGhlIGNvbnRlbnQgaW4gIFtTZWN0aW9uIDYgb2YgdGhlIERhdGEgVmlzdWFsaXNhdGlvbiBpbiBSIHN1cHBsZW1lbnRdKGh0dHBzOi8vYm9va2Rvd24ub3JnL3JlaGsvc3RtMTAwMV9kc21fdDFfZGF0YV92aXN1YWxpc2F0aW9uX2luX3IvY3JlYXRpbmctd2ViLWFwcHMtdXNpbmctc2hpbnktY29tcHV0ZXItbGFiLTRiLmh0bWwpIC0gaWYgeW91IGhhdmVuJ3QsIHRoZXNlIGxhYiBxdWVzdGlvbnMgd2lsbCBub3QgbWFrZSBzZW5zZS4NCg0KIyMNCg0KSWYgeW91IGhhdmUgbm90IGFscmVhZHkgaW5zdGFsbGVkIFNoaW55LCBwbGVhc2UgZG8gc28gbm93LCBhbmQgbG9hZCBTaGlueSBpbiB5b3VyIGN1cnJlbnQgUiBzZXNzaW9uLg0KDQoqSWYgeW91IG5lZWQgYSByZWZyZXNoZXIsIHlvdSBjYW4gY2hlY2sgdGhlIGBDb2RlYCBjaHVuayBiZWxvdy4qDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUiLCBldmFsID0gRiwgZWNobyA9IFR9DQppbnN0YWxsLnBhY2thZ2VzKCJzaGlueSIpDQpsaWJyYXJ5KHNoaW55KQ0KYGBgDQoNCiMjDQoNCk9rLCBsZXQncyBnZXQgc3RhcnRlZCBvbiBjcmVhdGluZyBvdXIgZmlyc3QgU2hpbnkgYXBwLg0KDQpUbyBiZWdpbiwgY3JlYXRlIGEgbmV3IGZvbGRlciBvbiB5b3VyIGNvbXB1dGVyIGNhbGxlZCBgc2hpbnlfYXBwX3Rlc3RgLCBhbmQgdGhlbiBzZXQgdGhpcyBmb2xkZXIgYXMgeW91ciBjdXJyZW50IHdvcmtpbmcgZGlyZWN0b3J5IGluIFIuDQoNCipIaW50OiBUbyBjaGVjayB5b3VyIGN1cnJlbnQgd29ya2luZyBkaXJlY3RvcnksIHJ1biB0aGUgY29tbWFuZCBgZ2V0d2QoKWAuIFRoZW4ganVzdCBtYWtlIHN1cmUgeW91IGhhdmUgY3JlYXRlZCBhIG5ldyBkaXJlY3RvcnkgZm9yIHlvdXIgYXBwIHdpdGhpbiB5b3VyIGN1cnJlbnQgd29ya2luZyBkaXJlY3RvcnkuKg0KDQojIyB7I2Jhc2Vjb2RlfQ0KDQpDb3B5IGFsbCB0aGUgUiBjb2RlIGluIHRoZSBgQ29kZWAgY2h1bmsgYmVsb3cgaW50byBhIG5ldyBSIHNjcmlwdCBmaWxlLCBhbmQgc2F2ZSB0aGlzIHNjcmlwdCBhcyBgYXBwLlJgIHdpdGhpbiB5b3VyIG5ldyBmb2xkZXIgYHNoaW55X2FwcF90ZXN0YC4NCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSIsIGV2YWwgPSBGLCBlY2hvID0gVH0NCmxpYnJhcnkoc2hpbnkpDQoNCiMgRGVmaW5lIFVJIGZvciBhcHAgdGhhdCBkcmF3cyBhIGhpc3RvZ3JhbSAtLS0tDQp1aSA8LSBmbHVpZFBhZ2UoDQoNCiAgIyBBcHAgdGl0bGUgLS0tLQ0KICB0aXRsZVBhbmVsKCJIZWxsbyBTaGlueSEiKSwNCg0KICAjIFNpZGViYXIgbGF5b3V0IHdpdGggaW5wdXQgYW5kIG91dHB1dCBkZWZpbml0aW9ucyAtLS0tDQogIHNpZGViYXJMYXlvdXQoDQoNCiAgICAjIFNpZGViYXIgcGFuZWwgZm9yIGlucHV0cyAtLS0tDQogICAgc2lkZWJhclBhbmVsKA0KDQogICAgICAjIElucHV0OiBTbGlkZXIgZm9yIHRoZSBudW1iZXIgb2YgYmlucyAtLS0tDQogICAgICBzbGlkZXJJbnB1dChpbnB1dElkID0gImJpbnMiLA0KICAgICAgICAgICAgICAgICAgbGFiZWwgPSAiTnVtYmVyIG9mIGJpbnM6IiwNCiAgICAgICAgICAgICAgICAgIG1pbiA9IDEsDQogICAgICAgICAgICAgICAgICBtYXggPSA1MCwNCiAgICAgICAgICAgICAgICAgIHZhbHVlID0gMzApDQoNCiAgICApLA0KDQogICAgIyBNYWluIHBhbmVsIGZvciBkaXNwbGF5aW5nIG91dHB1dHMgLS0tLQ0KICAgIG1haW5QYW5lbCgNCg0KICAgICAgIyBPdXRwdXQ6IEhpc3RvZ3JhbSAtLS0tDQogICAgICBwbG90T3V0cHV0KG91dHB1dElkID0gImRpc3RQbG90IikNCg0KICAgICkNCiAgKQ0KKQ0KDQojIERlZmluZSBzZXJ2ZXIgbG9naWMgcmVxdWlyZWQgdG8gZHJhdyBhIGhpc3RvZ3JhbSAtLS0tDQpzZXJ2ZXIgPC0gZnVuY3Rpb24oaW5wdXQsIG91dHB1dCkgew0KDQogICMgSGlzdG9ncmFtIG9mIHRoZSBPbGQgRmFpdGhmdWwgR2V5c2VyIERhdGEgLS0tLQ0KICAjIHdpdGggcmVxdWVzdGVkIG51bWJlciBvZiBiaW5zDQogICMgVGhpcyBleHByZXNzaW9uIHRoYXQgZ2VuZXJhdGVzIGEgaGlzdG9ncmFtIGlzIHdyYXBwZWQgaW4gYSBjYWxsDQogICMgdG8gcmVuZGVyUGxvdCB0byBpbmRpY2F0ZSB0aGF0Og0KICAjDQogICMgMS4gSXQgaXMgInJlYWN0aXZlIiBhbmQgdGhlcmVmb3JlIHNob3VsZCBiZSBhdXRvbWF0aWNhbGx5DQogICMgICAgcmUtZXhlY3V0ZWQgd2hlbiBpbnB1dHMgKGlucHV0JGJpbnMpIGNoYW5nZQ0KICAjIDIuIEl0cyBvdXRwdXQgdHlwZSBpcyBhIHBsb3QNCiAgb3V0cHV0JGRpc3RQbG90IDwtIHJlbmRlclBsb3Qoew0KDQogICAgeCAgICA8LSBmYWl0aGZ1bCR3YWl0aW5nDQogICAgYmlucyA8LSBzZXEobWluKHgpLCBtYXgoeCksIGxlbmd0aC5vdXQgPSBpbnB1dCRiaW5zICsgMSkNCg0KICAgIGhpc3QoeCwgYnJlYWtzID0gYmlucywgY29sID0gIiM3NUFBREIiLCBib3JkZXIgPSAid2hpdGUiLA0KICAgICAgICAgeGxhYiA9ICJXYWl0aW5nIHRpbWUgdG8gbmV4dCBlcnVwdGlvbiAoaW4gbWlucykiLA0KICAgICAgICAgbWFpbiA9ICJIaXN0b2dyYW0gb2Ygd2FpdGluZyB0aW1lcyIpDQoNCiAgICB9KQ0KDQp9DQoNCnNoaW55QXBwKHVpID0gdWksIHNlcnZlciA9IHNlcnZlcikNCmBgYA0KDQojIw0KDQpUbyBjaGVjayB0aGF0IGV2ZXJ5dGhpbmcgaXMgd29ya2luZywgb3BlbiBhIHNlcGFyYXRlIHNjcmlwdCBmaWxlLCBhbmQgcnVuIHRoZSBjb21tYW5kIGBydW5BcHAoImFwcC5SIilgLiANCg0KVGhlIGBIZWxsbyBTaGlueSFgIGV4YW1wbGUgU2hpbnkgYXBwIGRpc2N1c3NlZCBpbiBbU2VjdGlvbiA2IG9mIHRoZSBEYXRhIFZpc3VhbGlzYXRpb24gaW4gUiBzdXBwbGVtZW50XShodHRwczovL2Jvb2tkb3duLm9yZy9yZWhrL3N0bTEwMDFfZHNtX3QxX2RhdGFfdmlzdWFsaXNhdGlvbl9pbl9yL2NyZWF0aW5nLXdlYi1hcHBzLXVzaW5nLXNoaW55LWNvbXB1dGVyLWxhYi00Yi5odG1sKSBzaG91bGQgYXBwZWFyLiANCg0KRm9yIHRoZSBtb21lbnQsIGNsb3NlIHRoZSBhcHAgLSBpdCdzIHRpbWUgdG8gbWFrZSBzb21lIGNoYW5nZXMuDQoNCipOb3RlOiBJZiB0aGUgYXBwIGRvZXMgbm90IGFwcGVhciBpbiBhIG5ldyB3aW5kb3csIGFuZCB5b3UgcmVjZWl2ZSBhbiBlcnJvciBtZXNzYWdlLCB0cnkgZG91YmxlLWNoZWNraW5nIHRoZSBzdGVwcyBvdXRsaW5lZCBhYm92ZSwgY2hlY2tpbmcgdGhhdCB5b3VyIGBhcHAuUmAgc2NyaXB0IGNvbnRhaW5zIG9ubHkgdGhlIGNvZGUgcHJvdmlkZWQgaW4gdGhlIFxAcmVmKGJhc2Vjb2RlKSBjb2RlIGNodW5rLCBhbmQgY2hlY2tpbmcgdGhhdCB5b3UgaGF2ZSB0aGUgYHNoaW55YCBSIHBhY2thZ2UgaW5zdGFsbGVkLioNCg0KDQojIE1vZGlmeWluZyBhbiBleGlzdGluZyBTaGlueSBhcHANCg0KTm93IHRoYXQgd2UgaGF2ZSBhIHdvcmtpbmcgYGFwcC5SYCBzY3JpcHQsIGxldCdzIGJlZ2luIHRvIHRpbmtlciB3aXRoIHRoZSBjb2RlLg0KDQojIw0KDQpPcGVuIHVwIHlvdXIgYGFwcC5SYCBzY3JpcHQsIGFuZCBjaGFuZ2UgdGhlIHRpdGxlIG9mIHlvdXIgYXBwIGZyb20gIkhlbGxvIFNoaW55ISIgdG8gIkhlbGxvIERhdGEgU2NpZW5jZSIuDQoNCiMjDQoNCk5leHQsIGNoYW5nZSB0aGUgc2V0dGluZ3Mgb24gdGhlIHNsaWRlciwgc28gdGhlIG1pbmltdW0gaXMgMywgdGhlIG1heGltdW0gaXMgNDAsIGFuZCB0aGUgc3RhcnRpbmcgbnVtYmVyIGlzIDIwLg0KDQojIw0KDQpOZXh0LCBzZWUgaWYgeW91IGNhbiBjaGFuZ2UgdGhlIGNvbG91ciBvZiB0aGUgaGlzdG9ncmFtIHRvIGFub3RoZXIgY29sb3VyLg0KDQojIyANCg0KUmVjYWxsIHRoYXQgd2l0aGluIG91ciBgYXBwLlJgIHNjcmlwdCwgdGhlIGhpc3RvZ3JhbSBwcm9kdWNlZCBpbiB0aGUgYHNlcnZlcmAgZnVuY3Rpb24gaXMgYXNzaWduZWQgdG8gdGhlIG91dHB1dCBgZGlzdFBsb3RgIHZpYSBgb3V0cHV0JGRpc3RQbG90YC4gSW4gdGhlIGB1aWAgb2JqZWN0IHdpdGhpbiBvdXIgYGFwcC5SYCBzY3JpcHQsIGBkaXN0UGxvdGAgaXMgdGhlbiBjYWxsZWQgYXMgdGhlIG91dHB1dCB0byBiZSBwcmVzZW50ZWQgaW4gdGhlIHdlYiBhcHAsIHZpYSB0aGUgYHBsb3RPdXRwdXQob3V0cHV0SWQgPSAiZGlzdFBsb3QiKWAgZnVuY3Rpb24uIA0KSW4gb3RoZXIgd29yZHMsIGBvdXRwdXQkZGlzdFBsb3RgIG1hdGNoZXMgYHBsb3RPdXRwdXQob3V0cHV0SWQgPSAiZGlzdFBsb3QiKWAuDQoNClRyeSBjaGFuZ2luZyB0aGUgaGlzdG9ncmFtIG91dHB1dCBuYW1lIGZyb20gYGRpc3RQbG90YCB0byBgZGlzcGxheUhpc3RgLg0KDQoqSGludDogWW91IHdpbGwgaGF2ZSB0byBtYWtlIHRoaXMgY2hhbmdlIGluIHR3byBwbGFjZXMuKg0KDQojIw0KDQpXaGVuIHlvdSBhcmUgaGFwcHkgd2l0aCBhbGwgdGhlc2UgY2hhbmdlcywgcmVsYXVuY2ggeW91ciBhcHAgdG8gdmVyaWZ5IHRoYXQgeW91ciBjaGFuZ2VzIGhhdmUgd29ya2VkIGFzIGRlc2lyZWQuDQoNCiMgTWFraW5nIGEgU2hpbnkgYXBwIGZvciB0aGUgUGVuZ3VpbnMgZGF0YQ0KDQpOb3cgdGhhdCB3ZSBoYXZlIGZhbWlsaWFyaXNlZCBvdXJzZWx2ZXMgd2l0aCB0aGUgY29kZSBzdHJ1Y3R1cmUgb2YgYSBTaGlueSBhcHAsIGxldCdzIG1ha2UgYSBTaGlueSBhcHAgdGhhdCBkaXNwbGF5cyBgcGVuZ3VpbnNgIGRhdGEgZnJvbSB0aGUgYHBhbG1lcnBlbmd1aW5zYCBSIHBhY2thZ2UgW0BwZW5ndWluc10uDQpCeSBub3cgeW91IHNob3VsZCBiZSBxdWl0ZSBmYW1pbGlhciB3aXRoIHRoaXMgZGF0YSwgc28gd2Ugd29uJ3QgbmVlZCB0byBzcGVuZCBhbnkgdGltZSBleHBsb3JpbmcgdGhlIGNoYXJhY3RlcmlzdGljcyBvZiB0aGUgYHBlbmd1aW5zYCBkYXRhIHNldC4NCg0KTGV0J3MgYmVnaW4uDQoNCiMjDQoNCkZvbGxvd2luZyB0aGUgcHJvY2VzcyBvdXRsaW5lZCBpbiBbU2VjdGlvbiA2IG9mIHRoZSBEYXRhIFZpc3VhbGlzYXRpb24gaW4gUiBzdXBwbGVtZW50XShodHRwczovL2Jvb2tkb3duLm9yZy9yZWhrL3N0bTEwMDFfZHNtX3QxX2RhdGFfdmlzdWFsaXNhdGlvbl9pbl9yL2NyZWF0aW5nLXdlYi1hcHBzLXVzaW5nLXNoaW55LWNvbXB1dGVyLWxhYi00Yi5odG1sKSwgYW5kIHVzaW5nIHRoZSBjb2RlIGluIFxAcmVmKGJhc2Vjb2RlKSwgcHJvZHVjZSBhIHNpbXBsZSBTaGlueSB3ZWIgYXBwIHRoYXQgZGlzcGxheXMgYSBoaXN0b2dyYW0gb2YgdGhlIGZsaXBwZXIgbGVuZ3RoIChpbiBtbSkgb2YgdGhlIHBlbmd1aW5zIGluIHRoZSBQYWxtZXIgQXJjaGlwZWxhZ28uIFRoaXMgYXBwIHNob3VsZCBpbmNsdWRlIGEgc2xpZGVyIHNvIHRoYXQgdXNlcnMgY2FuIHNlbGVjdCB0aGUgbnVtYmVyIG9mIGJpbnMgZGlzcGxheWVkIGluIHRoZSBoaXN0b2dyYW0uDQoNCioqQXMgYSBndWlkZSwgdHJ5IHRvIGZvbGxvdyB0aGVzZSBzdGVwczoqKg0KDQphLiBDcmVhdGUgYSBuZXcgZGlyZWN0b3J5IGZvciB5b3VyIHBlbmd1aW5zIGFwcCAocmVtZW1iZXIgZWFjaCBhcHAgc2hvdWxkIGhhdmUgYSBzZXBhcmF0ZSBkaXJlY3RvcnkpLg0KYi4gQ3JlYXRlIHlvdXIgYGFwcC5SYCBzY3JpcHQgZmlsZSBhbmQgc2F2ZSBpdCBpbiB5b3VyIG5ldyBkaXJlY3RvcnkgKHVzZSB0aGUgY29kZSBpbiBcQHJlZihiYXNlY29kZSkgZm9yIHRoZSBiYXNlIHZlcnNpb24gb2YgdGhpcyBzY3JpcHQpLg0KYy4gTW9kaWZ5IHRoaXMgc2NyaXB0IGFzIHJlcXVpcmVkLiBEb24ndCB3b3JyeSBhYm91dCBjaGFuZ2luZyB0aGUgYmluIHNwZWNpZmljYXRpb25zIC0ganVzdCBmb2N1cyBvbiB0aGUgZGF0YSBiZWluZyB1c2VkLg0KZC4gQ2hhbmdlIHRoZSB0aXRsZSBvZiB0aGUgYXBwLCBhbmQgdGhlIGxhYmVscyBvbiB0aGUgaGlzdG9ncmFtLg0KDQoqTm90ZSB0aGF0IHlvdSB3aWxsIG5lZWQgdG8gbWFrZSBzdXJlIHRoYXQgeW91IGxvYWQgdGhlIGBwYWxtZXJwZW5ndWluc2AgcGFja2FnZSB3aXRoaW4geW91ciBgYXBwLlJgIGZpbGUuKg0KDQpPbmUgYXNwZWN0IG9mIHRoZSBgcGVuZ3VpbnNgIGRhdGEgc2V0IHdoaWNoIHdlIGhhdmUgYnJ1c2hlZCBvdmVyIGluIHByZXZpb3VzIGxhYnMgaXMgdGhhdCBpdCBjb250YWlucyBzZXZlcmFsIG1pc3NpbmcgdmFsdWVzLiBUaGVzZSBjYW4gY2F1c2UgZGlmZmljdWx0aWVzIHdpdGggb3VyIGNvZGUuIFRvIGF2b2lkIHRoaXMsIHBsZWFzZSBpbmNsdWRlIHRoZSBmb2xsb3dpbmcgbGluZSBvZiBjb2RlLCBkaXJlY3RseSBhZnRlciB0aGUgbGluZSBpbiB3aGljaCB5b3UgbG9hZCB0aGUgYHBhbG1lcnBlbmd1aW5zYCBwYWNrYWdlOg0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IEYsIGVjaG8gPSBUfQ0KcGVuZ3VpbnMgPC0gbmEub21pdChwZW5ndWlucykNCiMgdGhpcyByZW1vdmVzIHRoZSBOQSBvYnNlcnZhdGlvbnMgaW4gdGhlIHBlbmd1aW5zIGRhdGEgc2V0DQpgYGANCg0KIyMgeyNiYXNlaW1hZ2V9DQoNCk9uY2UgeW91IGFyZSBoYXBweSB3aXRoIHlvdXIgYXBwLCBydW4gaXQuDQpJZiBhbGwgZ29lcyB3ZWxsLCB5b3Ugc2hvdWxkIGVuZCB1cCB3aXRoIGFuIGFwcCB0aGF0IGxvb2tzIHNvbWV0aGluZyBsaWtlIHRoaXM6DQoNCmBgYHtyLCBmaWcxLCBlY2hvPUZBTFNFLCBvdXQud2lkdGg9IjkwJSIsIGZpZy5hbGlnbiA9ICdjZW50ZXInLCBmaWcuY2FwPSJFeGFtcGxlIFNoaW55IFdlYiBBcHAgZm9yIEhpc3RvZ3JhbSBvZiBQZW5ndWluIEZsaXBwZXIgTGVuZ3RoLiJ9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiU1RNMTAwMV9DTDRCX2ZsaXBwZXJfc2hpbnkuanBnIikNCmBgYA0KDQoqTm90ZTogSWYgeW91IGhhdmUgbWFkZSBhIGRlY2VudCBlZmZvcnQgdG8gY3JlYXRlIHRoaXMgYXBwLCBidXQganVzdCBjYW4ndCBzZWVtIHRvIGdldCBpdCBmdWxseSB3b3JraW5nLCB0YWtlIGEgbG9vayBhdCB0aGUgY29kZSBpbiB0aGUgYENvZGVgIGNodW5rIGJlbG93OioNCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSIsIGV2YWwgPSBGLCBlY2hvID0gVH0NCmxpYnJhcnkoc2hpbnkpDQpsaWJyYXJ5KHBhbG1lcnBlbmd1aW5zKQ0KcGVuZ3VpbnMgPC0gbmEub21pdChwZW5ndWlucykNCg0KIyBEZWZpbmUgVUkgZm9yIGFwcCB0aGF0IGRyYXdzIGEgaGlzdG9ncmFtIC0tLS0NCnVpIDwtIGZsdWlkUGFnZSgNCiAgDQogICMgQXBwIHRpdGxlIC0tLS0NCiAgdGl0bGVQYW5lbCgiUGFsbWVyIFBlbmd1aW5zIEZsaXBwZXIgTGVuZ3RoIiksDQogIA0KICAjIFNpZGViYXIgbGF5b3V0IHdpdGggaW5wdXQgYW5kIG91dHB1dCBkZWZpbml0aW9ucyAtLS0tDQogIHNpZGViYXJMYXlvdXQoDQogICAgDQogICAgIyBTaWRlYmFyIHBhbmVsIGZvciBpbnB1dHMgLS0tLQ0KICAgIHNpZGViYXJQYW5lbCgNCiAgICAgIA0KICAgICAgIyBJbnB1dDogU2xpZGVyIGZvciB0aGUgbnVtYmVyIG9mIGJpbnMgLS0tLQ0KICAgICAgc2xpZGVySW5wdXQoaW5wdXRJZCA9ICJiaW5zIiwNCiAgICAgICAgICAgICAgICAgIGxhYmVsID0gIk51bWJlciBvZiBiaW5zOiIsDQogICAgICAgICAgICAgICAgICBtaW4gPSAxLA0KICAgICAgICAgICAgICAgICAgbWF4ID0gNTAsDQogICAgICAgICAgICAgICAgICB2YWx1ZSA9IDMwKQ0KICAgICAgDQogICAgKSwNCiAgICANCiAgICAjIE1haW4gcGFuZWwgZm9yIGRpc3BsYXlpbmcgb3V0cHV0cyAtLS0tDQogICAgbWFpblBhbmVsKA0KICAgICAgDQogICAgICAjIE91dHB1dDogSGlzdG9ncmFtIC0tLS0NCiAgICAgIHBsb3RPdXRwdXQob3V0cHV0SWQgPSAiZmxpcHBlclBsb3QiKQ0KICAgICkNCiAgKQ0KKQ0KDQojIERlZmluZSBzZXJ2ZXIgbG9naWMgcmVxdWlyZWQgdG8gZHJhdyBhIGhpc3RvZ3JhbSAtLS0tDQpzZXJ2ZXIgPC0gZnVuY3Rpb24oaW5wdXQsIG91dHB1dCkgew0KICANCiAgb3V0cHV0JGZsaXBwZXJQbG90IDwtIHJlbmRlclBsb3Qoew0KICAgIA0KICAgIHggICAgPC0gcGVuZ3VpbnMkZmxpcHBlcl9sZW5ndGhfbW0NCiAgICBiaW5zIDwtIHNlcShtaW4oeCksIG1heCh4KSwgbGVuZ3RoLm91dCA9IGlucHV0JGJpbnMgKyAxKQ0KICAgIA0KICAgIGhpc3QoeCwgYnJlYWtzID0gYmlucywgY29sID0gIiM3NUFBREIiLCBib3JkZXIgPSAid2hpdGUiLA0KICAgICAgICAgeGxhYiA9ICJGbGlwcGVyIGxlbmd0aCBvZiBwZW5ndWlucyAobW0pIiwNCiAgICAgICAgIG1haW4gPSAiSGlzdG9ncmFtIG9mIFBlbmd1aW4gRmxpcHBlciBMZW5ndGhzIikNCiAgICANCiAgfSkNCiAgDQp9DQoNCnNoaW55QXBwKHVpID0gdWksIHNlcnZlciA9IHNlcnZlcikNCmBgYA0KDQojIEFkZGluZyB3aWRnZXRzIHRvIGEgU2hpbnkgYXBwDQoNCkEgd2lkZ2V0IGlzIGFuIGludGVyYWN0aXZlIHdlYiBlbGVtZW50LiBXaWRnZXRzIGhhdmUgYSBkZWZhdWx0IHZhbHVlLCBzcGVjaWZpZWQgaW4gb3VyIGBhcHAuUmAgc2NyaXB0LCBhbmQgdXNlcnMgY2FuIGNoYW5nZSB0aGlzIHZhbHVlIGJ5IGludGVyYWN0aW5nIHdpdGggdGhlIHdpZGdldC4gSW4gZmFjdCwgdGhlIGN1cnJlbnQgdmVyc2lvbiBvZiBvdXIgcGVuZ3VpbiBTaGlueSBhcHAgYWxyZWFkeSBmZWF0dXJlcyBhIHdpZGdldCAtIHRoZSBoaXN0b2dyYW0gYmluIHNsaWRlciENCg0KV2UgY2FuIGFkZCBtdWx0aXBsZSB3aWRnZXRzIHRvIG91ciBTaGlueSBhcHAsIHRvIGVuaGFuY2UgdXNlciBleHBlcmllbmNlLiBMZXQncyB0cnkgdGhpcyBub3cuDQoNCiMjDQoNClNoaW55IGNvbnRhaW5zIGEgZmFtaWx5IG9mIHByZS1idWlsdCB3aWRnZXRzLCBlYWNoIHdpdGggdGhlaXIgb3duIFIgZnVuY3Rpb24uIEZvciBvdXIgc2xpZGVyIGJhciwgd2UgdXNlZCB0aGUgd2lkZ2V0IGZ1bmN0aW9uIGBzbGlkZXJJbnB1dGAuIFNvbWUgb3RoZXIgb3B0aW9ucyAtIGFuZCB0aGVpciBjb3JyZXNwb25kaW5nIFIgZnVuY3Rpb25zIC0gaW5jbHVkZToNCg0KKiBDaGVja2JveCAoc2luZ2xlKSAtIGBjaGVja2JveElucHV0YA0KKiBDaGVja2JveCAoZ3JvdXApIC0gYGNoZWNrYm94R3JvdXBJbnB1dGANCiogTnVtZXJpYyBJbnB1dCAtIGBudW1lcmljSW5wdXRgDQoqIFRleHQgSW5wdXQgLSBgdGV4dElucHV0YA0KKiBEYXRlIFJhbmdlIC0gYGRhdGVSYW5nZUlucHV0YA0KKiBTZWxlY3QgQm94IC0gYHNlbGVjdElucHV0YA0KDQpBcyB5b3UgY2FuIHNlZSwgdGhlc2UgZnVuY3Rpb24gbmFtZXMgYXJlIGZhaXJseSBpbnR1aXRpdmUsIGFuZCBhbGwgY29udGFpbiB0aGUgZW5kaW5nIGBJbnB1dGAuIA0KDQojIw0KDQpGb3Igb3VyIGBhcHAuUmAgc2NyaXB0LCB3ZSB3aWxsIGZvY3VzIG9uIGFkZGluZyBhICpTZWxlY3QgQm94KiAodXNpbmcgYHNlbGVjdElucHV0YCksIHdoaWNoIGlzIGEgYm94IHdpdGggY2hvaWNlcyBmcm9tIHdoaWNoIHRvIHNlbGVjdC4NCg0KV2UgY2FuIGFkZCB3aWRnZXRzIHRvIG91ciBgYXBwLlJgIHNjcmlwdCBieSBwbGFjaW5nIHRoZSByZWxldmFudCBjb2RlIHdpdGhpbiBvbmUgb2YgdGhlIGBQYW5lbGAgc2VjdGlvbnMgb2Ygb3VyIGB1aWAgb2JqZWN0IChlaXRoZXIgdGhlIGBzaWRlYmFyUGFuZWwoKWAgb3IgYG1haW5QYW5lbCgpYCBzZWN0aW9ucykuDQoNCkVhY2ggd2lkZ2V0IGZ1bmN0aW9uIHRha2VzIGEgc2V0IG9mIHNwZWNpZmljIGFyZ3VtZW50cyAod2hpY2ggaXMgd2h5IHdlIGFyZSBvbmx5IGZvY3VzaW5nIG9uIGFkZGluZyBvbmUgcmlnaHQgbm93ISkuIFRoZSBmaXJzdCB0d28gYXJndW1lbnRzIGZvciBhbGwgd2lkZ2V0cyBhcmUgaG93ZXZlciB0aGUgc2FtZToNCg0KKiBUaGUgd2lkZ2V0IG11c3QgaGF2ZSBhIG5hbWUNCiogVGhlIHdpZGdldCBtdXN0IGhhdmUgYSBsYWJlbA0KDQpMZXQncyB0YWtlIGFub3RoZXIgbG9vayBhdCB0aGUgY29kZSBmb3IgdGhlIHNsaWRlciBiYXIgd2lkZ2V0IGZvciBvdXIgYXBwLg0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IEYsIGVjaG8gPSBUfQ0Kc2xpZGVySW5wdXQoaW5wdXRJZCA9ICJiaW5zIiwNCiAgICAgICAgICAgICAgICAgIGxhYmVsID0gIk51bWJlciBvZiBiaW5zOiIsDQogICAgICAgICAgICAgICAgICBtaW4gPSAxLA0KICAgICAgICAgICAgICAgICAgbWF4ID0gNTAsDQogICAgICAgICAgICAgICAgICB2YWx1ZSA9IDMwKSwNCmBgYA0KDQpZb3UgY2FuIHNlZSBoZXJlIHRoYXQgdGhlIGZpcnN0IGFyZ3VtZW50LCBgaW5wdXRJZCA9ICJiaW5zImAsIGlzIHRlbGxpbmcgUiB0aGF0IHRoZSBuYW1lIG9mIHRoaXMgd2lkZ2V0IGlzIGAiYmlucyJgIC0gdGhpcyBpcyB1c2VkIHRvIGFjY2VzcyB0aGUgdmFsdWUgb2YgdGhlIHdpZGdldC4gVGhpcyB2YWx1ZSBpcyBkZWZpbmVkIGluIHRoZSBgc2VydmVyYCBmdW5jdGlvbiwgdmlhIHRoZSBsaW5lIG9mIGNvZGUgYGJpbnMgPC0gc2VxKG1pbih4KSwgbWF4KHgpLCBsZW5ndGgub3V0ID0gaW5wdXQkYmlucyArIDEpYC4NCg0KVGhlIHNlY29uZCBhcmd1bWVudCwgYGxhYmVsID0gIk51bWJlciBvZiBiaW5zOiJgIHNwZWNpZmllcyB0aGUgd2lkZ2V0IGxhYmVsIHRoYXQgd2lsbCBhcHBlYXIgaW4geW91ciBhcHAuIA0KDQoqTm90ZTogWW91IGNhbiByZWZlciB0byBGaWd1cmUgXEByZWYoZmlnOmZpZzEpIHRvIGNoZWNrIHRoaXMgbGFiZWwuKg0KDQojIyB7I2JlZ2lubmluZ30NCg0KTGV0J3Mga2VlcCB0aGluZ3Mgc2ltcGxlLCBhbmQgdHJ5IHRvIGFkZCBhIHNjYXR0ZXIgcGxvdCB0byBvdXIgYXBwLiBGb3IgdGhpcyBzY2F0dGVyIHBsb3QsIHdlIHdvdWxkIGxpa2UgdG8gdXNlIGEgKlNlbGVjdCBCb3gqIHRvIHNlbGVjdCB0aGUgYHBlbmd1aW5zYCB2YXJpYWJsZSBwbG90dGVkIG9uIHRoZSB2ZXJ0aWNhbCBheGlzLiBGb3IgdGhlIGhvcml6b250YWwgYXhpcywgd2Ugd2lsbCB1c2UgdGhlIHZhcmlhYmxlIGBib2R5X21hc3NfZ2AuDQoNClRvIGJlZ2luLCB3ZSBuZWVkIHRvIGFkZCBhIGBzZWxlY3RJbnB1dGAgd2lkZ2V0IHRvIHRoZSBgdWlgIG9iamVjdCBzZWN0aW9uIG9mIHlvdXIgYGFwcC5SYCBzY3JpcHQuIFRoaXMgd2lkZ2V0IHNob3VsZCBhbGxvdyB1c2VycyB0byBzZWxlY3QgYmV0d2VlbiB0aGUgdmFyaWFibGVzIGBiaWxsX2xlbmd0aF9tbWAsIGBiaWxsX2RlcHRoX21tYCBhbmQgYGBmbGlwcGVyX2xlbmd0aF9tbWAuDQoNClVzZSB0aGUgY29kZSBiZWxvdyBhcyBhIGd1aWRlIHRvIGRvIHRoaXMgbm93LiBPbmNlIGNvbXBsZXRlZCwgcGxhY2UgdGhpcyBjaHVuayBvZiBjb2RlIHdpdGhpbiB0aGUgYHNpZGViYXJQYW5lbCgpYCBmdW5jdGlvbiwgYmVmb3JlIHRoZSBgc2xpZGVySW5wdXRgIGZ1bmN0aW9uLg0KDQoqSGludDogWW91IHdpbGwgaGF2ZSB0byByZXBsYWNlIHRoZSBgLi4uYHMgd2l0aCBhcHByb3ByaWF0ZSBjb2RlLioNCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBGLCBlY2hvID0gVH0NCiAgc2VsZWN0SW5wdXQoaW5wdXRJZCA9ICJzZWxlY3QiLCANCiAgICAgICAgICAgICAgICAgIGxhYmVsID0gIlBlbmd1aW4gVmFyaWFibGVzOiIsIA0KICAgICAgICAgICAgICAgICAgY2hvaWNlcyA9IGMoIkJpbGwgTGVuZ3RoIChtbSkiID0gImJpbGxfbGVuZ3RoX21tIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAuLi4pKSwNCmBgYA0KDQpOb3RlIHRoYXQgaGVyZToNCg0KKiBUaGUgaW5wdXQgcmVmZXJlbmNlIElEIGlzIGBzZWxlY3RgIChlcXVpdmFsZW50IHRvIGBiaW5zYCBmb3IgdGhlIGhpc3RvZ3JhbSBiaW4gc2xpZGVyKQ0KKiBUaGUgbGFiZWwgdGhhdCB3aWxsIGFwcGVhciBhYm92ZSB0aGUgd2lkZ2V0IGlzICJQZW5ndWluIFZhcmlhYmxlczoiDQoNCiMjDQoNCk9uY2UgeW91IGFyZSBoYXBweSB3aXRoIHlvdXIgY29kZSwgdHJ5IHJlLXJ1bm5pbmcgeW91ciBhcHAuDQpJZiBhbGwgZ29lcyB3ZWxsLCB5b3Ugc2hvdWxkIGVuZCB1cCB3aXRoIGEgbmV3IHdpZGdldCBpbiB5b3VyIGFwcCwgdGhhdCB3aGVuIGNsaWNrZWQgbG9va3Mgc29tZXRoaW5nIGxpa2UgdGhpczoNCg0KYGBge3IsIGZpZzIsIGVjaG89RkFMU0UsIG91dC53aWR0aD0iOTAlIiwgZmlnLmFsaWduID0gJ2NlbnRlcicsIGZpZy5jYXA9Ik1vZGlmaWVkIFNoaW55IFdlYiBBcHAgZm9yIFBlbmd1aW4gRGF0YS4ifQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoIlNUTTEwMDFfQ0w0Ql9zZWxlY3Rfc2hpbnkuanBnIikNCmBgYA0KDQojIE1ha2luZyBSZWFjdGl2ZSBPdXRwdXRzIGZvciBTaGlueQ0KDQpIYW5nIG9uIGEgbWludXRlIHRob3VnaC4uLndlJ3ZlIGFkZGVkIGEgd2lkZ2V0IGJ1dCBub3RoaW5nJ3MgaGFwcGVuaW5nIQ0KDQpUaGlzIGlzIGJlY2F1c2Ugd2UgaGF2ZW4ndCB5ZXQgY29kZWQgb3VyIGFwcCB0byBoYXZlIHJlYWN0aXZlIG91dHB1dCwgYmFzZWQgb24gd2hhdCB0aGUgdXNlciBzZWxlY3RzIGZyb20gb3VyICpTZWxlY3QgQm94KiB3aWRnZXQuIExldCdzIHJlY3RpZnkgdGhhdCBub3cuDQoNCiMjIHsjZGlzdHBsb3Rjb21tYW5kfQ0KDQpSZWNhbGwgdGhhdCB3ZSB1c2UgdGhlIGBwbG90T3V0cHV0KG91dHB1dElkID0gImRpc3RQbG90IiksYCBjb21tYW5kIHdpdGhpbiB0aGUgYG1haW5QYW5lbGAgc2VjdGlvbiBvZiBvdXIgYHVpYCBvYmplY3QgdG8gdGVsbCBvdXIgYXBwIHRvIGRpc3BsYXkgdGhlIG9iamVjdCBgZGlzdFBsb3RgLg0KDQpJZiB3ZSB3YW50IG91ciBuZXcgd2lkZ2V0IHRvIGNoYW5nZSB0aGUgdmFyaWFibGVzIHBsb3R0ZWQgaW4gb3VyIHNjYXR0ZXIgcGxvdCwgd2UgbXVzdCB0ZWxsIFIgdGhhdCB3ZSB3YW50IGEgc2NhdHRlciBwbG90IHRvIGJlIGRpc3BsYXllZCENCg0KTGV0J3MgY2FsbCB0aGlzIHNjYXR0ZXIgcGxvdCBvYmplY3QgYHNjYXR0ZXJQbG90YC4gDQoNClVzaW5nIHRoZSBjb2RlIGNvbW1hbmQgc2hvd24gZWFybGllciBpbiBcQHJlZihkaXN0cGxvdGNvbW1hbmQpIGZvciBgZGlzdFBsb3RgLCBhZGQgYSBsaW5lIG9mIGNvZGUgaW4gdGhlIGBtYWluUGFuZWxgIHNlY3Rpb24gdG8gdGVsbCBSIHRvIGRpc3BsYXkgdGhlIG9iamVjdCBgc2NhdHRlclBsb3RgLg0KDQoqSGludDogWW91IGNhbiBjaGVjayB0aGUgYENvZGVgIGNodW5rIGJlbG93IGlmIHlvdSBuZWVkIHRvIGRvdWJsZS1jaGVjayB5b3VyIHdvcmsuKg0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIiwgZXZhbCA9IEYsIGVjaG8gPSBUfQ0KcGxvdE91dHB1dChvdXRwdXRJZCA9ICJzY2F0dGVyUGxvdCIpLA0KYGBgDQoNCiMjDQoNCkdyZWF0ISBOb3cgYWxsIHdlIG5lZWQgdG8gZG8gaXMgaW5jbHVkZSBzb21lIGNvZGUgaW4gdGhlIGBzZXJ2ZXJgIGZ1bmN0aW9uIHNlY3Rpb24gb2Ygb3VyIGBhcHAuUmAgc2NyaXB0LCB0byBhY3R1YWxseSBjYXJyeSBvdXQgdGhlIGNhbGN1bGF0aW9ucyB0byBwbG90IHRoZSBzY2F0dGVyIHBsb3QuDQoNCldlIHdpbGwga2VlcCB0aGlzIHNpbXBsZS4gUmVhZCBvdmVyIHRoZSBjb2RlIGJlbG93LCBhbmQgdGhlbiBhZGQgaXQgaW5zaWRlIHlvdXIgYHNlcnZlcmAgZnVuY3Rpb24uDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gRiwgZWNobyA9IFR9DQpvdXRwdXQkc2NhdHRlclBsb3QgPC0gcmVuZGVyUGxvdCh7DQoNCiAgdyA8LSBwZW5ndWluc1ssIGMoImJvZHlfbWFzc19nIiwgaW5wdXQkc2VsZWN0KSwgZHJvcCA9IEZBTFNFXQ0KICANCiAgcGxvdCh3LCBjb2wgPSAiIzc1QUFEQiIpDQogIA0KfSkNCmBgYA0KDQpOb3RlIHRoYXQganVzdCBhcyB3ZSBjYWxsIHRoZSB1c2VyIGlucHV0IHZhbHVlIGZvciB0aGUgaGlzdG9ncmFtIGJpbiBzbGlkZXIgdmlhIGBpbnB1dCRiaW5gLCB3ZSBjYW4gY2FsbCB0aGUgdXNlciBpbnB1dCB2YWx1ZSBmb3Igb3VyIHNjYXR0ZXIgcGxvdCBzZWxlY3QgYm94IGhlcmUgdmlhIGBpbnB1dCRzZWxlY3RgICh0YWtlIGEgbG9vayBiYWNrIGF0IFxAcmVmKGJlZ2lubmluZykgaWYgdGhpcyBkb2Vzbid0IG1ha2Ugc2Vuc2UpLg0KDQojIw0KDQpXZSBzaG91bGQgbm93IGJlIGdvb2QgdG8gZ28uIFRyeSByZS1ydW5uaW5nIHlvdXIgYXBwLiBJZiBhbGwgZ29lcyBhY2NvcmRpbmcgdG8gcGxhbiwgeW91IHNob3VsZCBub3cgaGF2ZSBhIHJlYWN0aXZlIHNjYXR0ZXIgcGxvdCBpbmNsdWRlZCBpbiB5b3VyIGFwcCENCg0KIyBNb2RpZnlpbmcgdGhlIFNoaW55IFVzZXIgSW50ZXJmYWNlDQoNCkV4Y2VsbGVudCEgV2Ugbm93IGhhdmUgYSByZXNwb25zaXZlIHNjYXR0ZXIgcGxvdCBkaXNwbGF5aW5nIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgUGFsbWVyIFBlbmd1aW5zJyBib2R5IG1hc3NlcyBhbmQgb3RoZXIgbWVhc3VyZW1lbnRzLiBIb3dldmVyLCBvdXIgdXNlciBpbnRlcmZhY2UgaXMgbG9va2luZyBhIGxpdHRsZSBiYXNpYyAtIGxldCdzIGFkZCBzb21lIGRldGFpbHMgdG8gZmluaXNoIHVwLg0KDQpUaGVyZSBhcmUgbWFueSBTaGlueSB1c2VyIGludGVyZmFjZSBvcHRpb25zIGF2YWlsYWJsZSB0byB1cywgYW5kIHdlIHdvbid0IGhhdmUgdGltZSB0byBjb3ZlciBhbGwgdGhlbSBpbiB0aGlzIGxhYiwgc28gcGxlYXNlIGtlZXAgaW4gbWluZCB0aGF0IHRoZSBmb2xsb3dpbmcgb3B0aW9ucyBhcmUganVzdCBhIHNlbGVjdGlvbiBmcm9tIGEgd2lkZXIgc2V0Lg0KDQojIw0KDQpUaGUgYGZsdWlkUGFnZWAgZnVuY3Rpb24gd2l0aGluIHRoZSBgdWlgIG9iamVjdCBvZiBvdXIgYGFwcC5SYCBzY3JpcHQgaXMgdXNlZCB0byBjcmVhdGUgYSBkeW5hbWljIGRpc3BsYXkgZm9yIG91ciBTaGlueSBhcHAgLSBpLmUuIGl0IHdpbGwgYWRqdXN0IHRvIHRoZSBkaW1lbnNpb25zIG9mIHlvdXIgdXNlcidzIHNjcmVlbi4gDQoNCkJ5IHBsYWNpbmcgZGlmZmVyZW50IGVsZW1lbnRzIHdpdGhpbiB0aGlzIGZ1bmN0aW9uLCB3ZSBjYW4gY2hhbmdlIHRoZSBhcHBlYXJhbmNlIGFuZCBsYXlvdXQgb2Ygb3VyIGFwcC4NCg0KUmVjYWxsIHRoYXQgd2UgaGF2ZSB0aHJlZSBwYW5lbHMgaW4gYSBTaGlueSBhcHAgKHRoZSB0aXRsZSBwYW5lbCwgdGhlIHNpZGUgcGFuZWwsIGFuZCB0aGUgbWFpbiBwYW5lbCkuDQoNCkN1cnJlbnRseSwgb3VyIFBlbmd1aW4gYXBwIGhhcyBhIHNpZGViYXIgb24gdGhlIGxlZnQtaGFuZCBzaWRlLiBUcnkgY2hhbmdpbmcgdGhpcyBub3csIGJ5IGluY2x1ZGluZyB0aGUgZWxlbWVudCBgcG9zaXRpb24gPSAicmlnaHQiYCBpbnNpZGUgdGhlIGBzaWRlYmFyTGF5b3V0KClgIGZ1bmN0aW9uLg0KDQojIyANCg0KVHJ5IHN3aXRjaGluZyB0aGUgcG9zaXRpb25zIG9mIHRoZSB3aWRnZXRzIGluIHRoZSBzaWRlYmFyLCBieSBzd2FwcGluZyB0aGVpciBjb2RlIGNodW5rcyBpbiB0aGUgYHVpYCBvYmplY3QgKHlvdSBzaG91bGQgYmUgYWJsZSB0byBzZWxlY3QgdGhlIHdob2xlIGNodW5rIGNvcnJlc3BvbmRpbmcgdG8gdGhlIHdpZGdldCwgY3V0IGl0IGFuZCB0aGVuIHBhc3RlIGl0IGFib3ZlL2JlbG93IHRoZSBjb2RlIGNodW5rIGZvciB0aGUgb3RoZXIgd2lkZ2V0KQ0KDQoqTm90ZTogTWFrZSBzdXJlIHlvdSBoYXZlIGEgY29tbWEgb25seSBhdCB0aGUgZW5kIG9mIHRoZSBmaXJzdCB3aWRnZXQgY29kZSBjaHVuay4qDQoNCiMjIEZvcm1hdHRlZCB0ZXh0DQoNCldlIGNhbiBhZGQgdGV4dCB0byBvdXIgZGlmZmVyZW50IHBhbmVscywgdG8gcHJvdmlkZSBkZXRhaWxzIGFib3V0IG91ciBkYXRhLiANCg0KVG8gYWRkIGhlbHBmdWwgdGV4dCBhYm92ZSBvciBiZWxvdyBhIHdpZGdldCAodG8gZGVzY3JpYmUgdGhlIHB1cnBvc2Ugb2YgdGhlIHdpZGdldCksIHdlIGNhbiB1c2UgdGhlIGBoZWxwVGV4dGAgZnVuY3Rpb24sIHdpdGhpbiB0aGUgYHNpZGViYXJQYW5lbGAgZnVuY3Rpb24uIFRvIGRpc3BsYXkgdGhlIHRleHQgYWJvdmUgdGhlIHdpZGdldCwgcHV0IHRoZSBgaGVscFRleHRgIGNvZGUgYWJvdmUgdGhlIHdpZGdldC4gVG8gZGlzcGxheSB0aGUgdGV4dCBiZWxvdyB0aGUgd2lkZ2V0LCBwdXQgdGhlIGBoZWxwVGV4dGAgY29kZSBiZWxvdyB0aGUgd2lkZ2V0IC0gc2ltcGxlIGFzIHRoYXQuDQoNCipUYWtlIGEgbG9vayBhdCB0aGUgY29kZSBiZWxvdyBmb3IgYW4gZXhhbXBsZS4qDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gRiwgZWNobyA9IFR9DQpoZWxwVGV4dCgidGhpcyBpcyBzb21lIGhlbHBmdWwgdGV4dCB0byBkZXNjcmliZSBhIHdpZGdldCIpLA0KYGBgDQoNCiMjDQoNCklmIHdlIHdvdWxkIGxpa2UgdG8gYWRkIGZvcm1hdHRlZCB0ZXh0LCBzdWNoIGFzIGEgcGFyYWdyYXBoLCB3ZSBjYW4gdXNlIHRoZSBgcGAgZnVuY3Rpb24sIHdpdGhpbiB0aGUgcmVsZXZhbnQgcGFuZWwuDQoNCipUYWtlIGEgbG9vayBhdCB0aGUgY29kZSBiZWxvdyBmb3IgYW4gZXhhbXBsZS4qDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gRiwgZWNobyA9IFR9DQpwKCJ1c2luZyBwIGNyZWF0ZXMgYSBwYXJhZ3JhcGggb2YgdGV4dC4iKSwNCmBgYA0KDQojIw0KDQpGaW5hbGx5LCB3ZSBjYW4gYWxzbyBhZGQgaW1hZ2VzIHRvIG91ciBhcHAuDQpUaGlzIGlzIHJlbGF0aXZlbHkgc2ltcGxlLCBidXQgdGhlcmUgaXMgb25lIHN0ZXAgd2UgbXVzdCBwZXJmb3JtIGZpcnN0Lg0KQW55IGltYWdlIGZpbGUgd2UgYWRkIG11c3QgYmUgaW4gYSBmb2xkZXIgbmFtZWQgYHd3d2Agd2l0aGluIHRoZSBkaXJlY3RvcnkgY29udGFpbmluZyB5b3VyIGBhcHAuUmAgc2NyaXB0Lg0KDQphLiBNYWtlIHRoaXMgZm9sZGVyIG5vdy4NCmIuIFNlYXJjaCB0aGUgW0NyZWF0aXZlIENvbW1vbnMgaW1hZ2UgZGF0YWJhc2VdKGh0dHBzOi8vc2VhcmNoLmNyZWF0aXZlY29tbW9ucy5vcmcvKSBmb3IgYSBwZW5ndWluIGltYWdlLg0KYy4gU2F2ZSBpdCB3aXRoIHRoZSBmaWxlIG5hbWUgYHBlbmd1aW4uanBnYCBpbiB5b3VyIGB3d3dgIGZvbGRlci4NCg0KIyMNCg0KVG8gaW5zZXJ0IHRoZSBpbWFnZSwgd2UgY2FuIHVzZSB0aGUgY29kZSBgaW1nKHNyYyA9ICJwZW5ndWluLmpwZyIpYCBpbiBhIHN1aXRhYmxlIHBsYWNlIHdpdGhpbiBvdXIgYGFwcC5SYCBzY3JpcHQgLSBmb3IgaW5zdGFuY2Ugd2l0aGluIHRoZSBgbWFpblBhbmVsYCBzZWN0aW9uLg0KDQojIw0KDQpUaGUgaW1hZ2Ugc2l6ZSBtaWdodCBiZSB0b28gbGFyZ2Ugb3IgdG9vIHNtYWxsIC0gd2UgY2FuIG1vZGlmeSB0aGUgaGVpZ2h0IGFuZCB3aWR0aCBkaW1lbnNpb25zIChpbiBwaXhlbHMpIHVzaW5nIHRoZSBhZGRpdGlvbmFsIGNvbW1hbmRzIGBoZWlnaHRgIGFuZCBgd2lkdGhgICh3aGljaCBzZWVtcyBsb2dpY2FsKSBpbiB0aGUgYGltZygpYCBmdW5jdGlvbi4NCg0KKlRha2UgYSBsb29rIGF0IHRoZSBjb2RlIGJlbG93IGZvciBhbiBleGFtcGxlLioNCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBGLCBlY2hvID0gVH0NCmltZyhzcmMgPSAicGVuZ3Vpbi5qcGciLCBoZWlnaHQgPSAzMDApLA0KYGBgDQoNClVzaW5nIHRoaXMgaW5mb3JtYXRpb24sIHRyeSB0byBtYWtlIHlvdXIgaW1hZ2UgZml0IG1vcmUgbmljZWx5IHdpdGhpbiB5b3VyIGFwcCBieSBjaGFuZ2luZyBpdHMgZGlzcGxheSBkaW1lbnNpb25zLg0KDQojIyBDaGFsbGVuZ2UNClRvIHRlc3QgeW91ciB1bmRlcnN0YW5kaW5nIG9mIHRoZSBpbmZvcm1hdGlvbiBwcmVzZW50ZWQgYWJvdmUsIHVzZSB0aGUgdmFyaW91cyBmdW5jdGlvbnMgYW5kIGVsZW1lbnRzIHdlIGhhdmUganVzdCBjb3ZlcmVkIHRvIHRyeSBhbmQgcmVjcmVhdGUgdGhlIFNoaW55IGFwcCBwaWN0dXJlZCBiZWxvdy4NCg0KYGBge3IsIGZpZzMsIGVjaG89RkFMU0UsIG91dC53aWR0aD0iMTAwJSIsIGZpZy5hbGlnbiA9ICdjZW50ZXInLCBmaWcuY2FwPSJJbXByb3ZlZCBTaGlueSBXZWIgQXBwIGZvciBQZW5ndWluIERhdGEuIn0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJTVE0xMDAxX0NMNEJfZmluYWxfc2hpbnkuanBnIikNCmBgYA0KDQo8YnI+DQoNCkRvbid0IHdvcnJ5IGlmIHlvdSBjYW4ndCBnZXQgYWxsIHRoZSBlbGVtZW50cyB0byB3b3JrIChhbmQgb2YgY291cnNlIHlvdXIgcGVuZ3VpbiBpbWFnZSBtYXkgYmUgZGlmZmVyZW50KS4NCkEgY29weSBvZiB0aGUgYGFwcC5SYCBzY3JpcHQgdXNlZCB0byBnZW5lcmF0ZSB0aGUgYXBwIHNob3duIGJlbG93IHdpbGwgYmUgaW5jbHVkZWQgaW4gdGhlIHNvbHV0aW9ucyB0byB0aGlzIGNvbXB1dGVyIGxhYiwgc28geW91IGNhbiBjaGVjayB0aGF0IGlmIHRoZXJlIHdlcmUgYW55IHBhcnRzIHdoaWNoIHlvdSB3ZXJlIHVuc3VyZSBhYm91dC4NCg0KIyBQdWJsaXNoaW5nIHlvdXIgU2hpbnkgQXBwDQoNCkZvciBkZXRhaWxzIG9uIGhvdyB0byBwdWJsaXNoIHlvdXIgU2hpbnkgYXBwIG9ubGluZSwgcGxlYXNlIHJlZmVyIHRvIFtTZWN0aW9uIDYgb2YgdGhlIERhdGEgVmlzdWFsaXNhdGlvbiBpbiBSIHN1cHBsZW1lbnRdKGh0dHBzOi8vYm9va2Rvd24ub3JnL3JlaGsvc3RtMTAwMV9kc21fdDFfZGF0YV92aXN1YWxpc2F0aW9uX2luX3IvY3JlYXRpbmctd2ViLWFwcHMtdXNpbmctc2hpbnktY29tcHV0ZXItbGFiLTRiLmh0bWwpLg0KDQo8YnI+DQoNCiMjIyMgR3JlYXQgam9iLCB0aGF0J3MgZXZlcnl0aGluZyBmb3IgdG9kYXkhICMjIyMgey19DQoNClRoYXQgd2FzIGEgbG90IHRvIGNvdmVyLCBidXQgaG9wZWZ1bGx5IHRoaXMgbGFiIGhhcyBpbnNwaXJlZCB5b3UgdG8gbGVhcm4gbW9yZSBTaGlueSBza2lsbHMgaW4gdGhlIGZ1dHVyZS4NCg0KRG8geW91IGhhdmUgYSBwcmVmZXJlbmNlIGZvciBQbG90bHkgb3IgU2hpbnk/DQoNClJlbWVtYmVyLCB5b3UgY2FuIGFsd2F5cyByZWZlciBiYWNrIHRvIHRoaXMgbWF0ZXJpYWwgYXQgYSBsYXRlciBkYXRlIGlmIHlvdSBuZWVkIGEgcXVpY2sgcmVmcmVzaGVyLg0KDQo8YnI+DQoNCiMgUmVmZXJlbmNlcyB7LSAjUmVmfQ0KPGRpdiBpZD0icmVmcyI+PC9kaXY+DQoNCjxicj4NCg0KPGZvbnQgY29sb3IgPSAiZ3JleSI+DQpUaGVzZSBub3RlcyBoYXZlIGJlZW4gcHJlcGFyZWQgYnkgUnVwZXJ0IEt1dmVrZS4gUGxlYXNlIG5vdGUgdGhhdCBzb21lIG9mIHRoZSBjb250ZW50IGluIHRoZXNlIG5vdGVzIGhhcyBiZWVuIGRldmVsb3BlZCBmcm9tIFsqdGhlIFIgU3R1ZGlvIFNoaW55IFR1dG9yaWFsKl0oaHR0cHM6Ly9zaGlueS5yc3R1ZGlvLmNvbS90dXRvcmlhbC8pIFtAcnN0dWRpb10uIFRoZSBjb3B5cmlnaHQgZm9yIHRoZSBtYXRlcmlhbCBpbiB0aGVzZSBub3RlcyByZXNpZGVzIHdpdGggdGhlIGF1dGhvcnMgbmFtZWQgYWJvdmUsIHdpdGggdGhlIERlcGFydG1lbnQgb2YgTWF0aGVtYXRpY2FsIGFuZCBQaHlzaWNhbCBTY2llbmNlcyBhbmQgd2l0aCBMYSBUcm9iZSBVbml2ZXJzaXR5LiBDb3B5cmlnaHQgaW4gdGhpcyB3b3JrIGlzIHZlc3RlZCBpbiBMYSBUcm9iZSBVbml2ZXJzaXR5IGluY2x1ZGluZyBhbGwgTGEgVHJvYmUgVW5pdmVyc2l0eSBicmFuZGluZyBhbmQgbmFtaW5nLiBVbmxlc3Mgb3RoZXJ3aXNlIHN0YXRlZCwgbWF0ZXJpYWwgd2l0aGluIHRoaXMgd29yayBpcyBsaWNlbnNlZCB1bmRlciBhIENyZWF0aXZlIENvbW1vbnMgQXR0cmlidXRpb24tTm9uIENvbW1lcmNpYWwtTm9uIERlcml2YXRpdmVzIExpY2Vuc2UgDQo8YSBocmVmID0gImh0dHBzOi8vY3JlYXRpdmVjb21tb25zLm9yZy9saWNlbnNlcy9ieS1uYy1uZC80LjAvQ0MiIHRhcmdldD0iX2JsYW5rIj4gQlktTkMtTkQuIDwvYT4NCjwvZm9udD4=