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!

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

1.1

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)

1.2

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.

1.3

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)

1.4

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.

2 Modifying an existing Shiny app

Now that we have a working app.R script, let’s begin to tinker with the code.

2.1

Open up your app.R script, and change the title of your app from “Hello Shiny!” to “Hello Data Science”.

2.2

Next, change the settings on the slider, so the minimum is 3, the maximum is 40, and the starting number is 20.

2.3

Next, see if you can change the colour of the histogram to another colour.

2.4

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.

2.5

When you are happy with all these changes, relaunch your app to verify that your changes have worked as desired.

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

3.1

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:

  1. Create a new directory for your penguins app (remember each app should have a separate directory).
  2. 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).
  3. Modify this script as required. Don’t worry about changing the bin specifications - just focus on the data being used.
  4. 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

3.2

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:

Example Shiny Web App for Histogram of Penguin Flipper Length.

Figure 3.1: Example Shiny Web App for Histogram of Penguin Flipper Length.

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)

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

4.1

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.

4.2

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.

4.3

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:”

4.4

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:

Modified Shiny Web App for Penguin Data.

Figure 4.1: Modified Shiny Web App for Penguin Data.

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

5.1

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"),

5.2

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

5.3

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!

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

6.1

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.

6.2

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.

6.3 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"),

6.4

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."),

6.5

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.

  1. Make this folder now.
  2. Search the Creative Commons image database for a penguin image.
  3. Save it with the file name penguin.jpg in your www folder.

6.6

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.

6.7

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.

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

Improved Shiny Web App for Penguin Data.

Figure 6.1: Improved Shiny Web App for Penguin Data.


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.

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