Data Science Module

Topic 4B: Data Visualisation IV


Example R code solutions for the Data Science Computer Lab 4, which uses data from the palmerpenguins R package (Horst, Hill, and Gorman 2020), and the shiny R package (Chang et al. 2021), are presented below.

This computer lab was designed to run alongside the content in Section 6 of the Data Visualisation in R supplement. It might be helpful to have this content open while you look through these solutions.

1 Preparations

1.1

install.packages("shiny")
library(shiny)

1.2

In this step, you needed to create a new folder called shiny_app_test within your current working directory.

For example, if your current working directory was "C:/Users/Jerry/Desktop/STM1001" your Shiny app directory would be "C:/Users/Jerry/Desktop/STM1001/shiny_app_test".

1.3

The R code below should have been saved in a script named 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

Running the code below in a new R script file should have led to the appearance of the Hello Shiny! example Shiny app discussed in Section 6 of the Data Visualisation in R supplement.

runApp("app.R")

2 Modifying an existing Shiny app

2.1

The line of code

  titlePanel("Hello Shiny!"),

should have been changed to

  titlePanel("Hello Data Science"),

2.2

The code chunk

 sliderInput(inputId = "bins",
                  label = "Number of bins:",
                  min = 1,
                  max = 50,
                  value = 30)

should have been changed to

 sliderInput(inputId = "bins",
                  label = "Number of bins:",
                  min = 3,
                  max = 40,
                  value = 20)

2.3

The code chunk

  hist(x, breaks = bins, col = "#75AADB", border = "white",
         xlab = "Waiting time to next eruption (in mins)",
         main = "Histogram of waiting times")

should have been changed, for example, to

  hist(x, breaks = bins, col = "red", border = "white",
         xlab = "Waiting time to next eruption (in mins)",
         main = "Histogram of waiting times")

2.4

Within the ui object, the line of code

      plotOutput(outputId = "distPlot")

should have been changed to

plotOutput(outputId = "displayHist")

and within the server function, the line of code

  output$distPlot <- renderPlot({

should have been changed to

  output$displayHist <- renderPlot({

2.5

No answer required. If all went well, your modified app should have appeared.

3 Making a Shiny app for the Penguins data

3.1

The full code for the requested app.R script is provided 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 = "distPlot"),
    )
  )
)

# Define server logic required to draw a histogram ----
server <- function(input, output) {
  
  output$distPlot <- 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)

3.2

No answer required. Note you can run the code provided above (once saved in an appropriate directory) to generate the requested app.

4 Adding widgets to a Shiny app

4.1

No answer required.

4.2

No answer required.

4.3

An example of the completed code chunk is

      selectInput(inputId = "select", 
                  label = "Penguin Variables:", 
                  choices = c("Bill Length (mm)" = "bill_length_mm", 
                              "Bill Depth (mm)" = "bill_depth_mm",
                              "Flipper Length (mm)" = "flipper_length_mm")),

and once it is placed in the appropriate position with the app.R script, this script should look like the code shown 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(
      
      selectInput(inputId = "select", 
                  label = "Penguin Variables:", 
                  choices = c("Bill Length (mm)" = "bill_length_mm", 
                              "Bill Depth (mm)" = "bill_depth_mm",
                              "Flipper Length (mm)" = "flipper_length_mm")),
      
      # 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) {
  
  output$distPlot <- 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.4

No answer required.

5 Making Reactive Outputs for Shiny

5.1

Once added, your app.R script should look like this:

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(
      
      selectInput(inputId = "select", 
                  label = "Penguin Variables:", 
                  choices = c("Bill Length (mm)" = "bill_length_mm", 
                              "Bill Depth (mm)" = "bill_depth_mm",
                              "Flipper Length (mm)" = "flipper_length_mm")),
      
      # 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"),
      
      plotOutput(outputId = "scatterPlot")
    )
  )
)

# Define server logic required to draw a histogram ----
server <- function(input, output) {
  
  output$distPlot <- 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)

5.2

Once you have added the code, your app.R script should look like this:

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(
      
      selectInput(inputId = "select", 
                  label = "Penguin Variables:", 
                  choices = c("Bill Length (mm)" = "bill_length_mm", 
                              "Bill Depth (mm)" = "bill_depth_mm",
                              "Flipper Length (mm)" = "flipper_length_mm")),
      
      # 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"),
      
      plotOutput(outputId = "scatterPlot")
      
    )
  )
)

# Define server logic required to draw a histogram ----
server <- function(input, output) {
  
  output$distPlot <- 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")
    
  })
  
  output$scatterPlot <- renderPlot({
    
    w <- penguins[, c("body_mass_g", input$select), drop = FALSE]
    
    plot(w, col = "#75AADB")
    
  })
  
}

shinyApp(ui = ui, server = server)

5.3

No answer required.

6 Modifying the Shiny User Interface

6.1

To change the sidebar position, the line of code

  sidebarLayout(

should now look like this:

    sidebarLayout(position = "right",

6.3 Formatted text

An example solution is provided below:

    sidebarPanel(
      
            # Input: Slider for the number of bins ----
            
      helpText("This is a slider for the number of bins. 
               This text will appear above the slider."),
      
      sliderInput(inputId = "bins",
                  label = "Number of bins:",
                  min = 1,
                  max = 50,
                  value = 30),
      
      selectInput(inputId = "select", 
                  label = "Penguin Variables:", 
                  choices = c("Bill Length (mm)" = "bill_length_mm", 
                              "Bill Depth (mm)" = "bill_depth_mm",
                              "Flipper Length (mm)" = "flipper_length_mm")),
      
      helpText("This is a selection box, 
               where you can select the Penguin variable you would like to plot. 
               This text will appear below the selection box.")
      
      ),

6.4

No answer required.

6.5

As an example, if your Shiny app directory is "C:/Users/Jerry/Desktop/STM1001/penguins_shiny_app" then you would create your www folder within this directory, so your image is saved to "C:/Users/Jerry/Desktop/STM1001/penguins_shiny_app/www".

6.6

As an example, you could have included the line of code

img(src = "penguin.jpg", height = 200, width = 300),

between the code for the two widgets shown in 6.2.

6.7

No answer required.

6.8 Challenge

The complete app.R script for the Challenge Shiny app is included below.

library(shiny)
library(palmerpenguins)
penguins <- na.omit(penguins)

# Define UI for app that draws a histogram ----
ui <- fluidPage(
  
  # App title ----
  titlePanel("Palmer Penguins Data"),
  
  # Sidebar layout with input and output definitions ----
  sidebarLayout(position = "right",
    
    # Sidebar panel for inputs ----
    sidebarPanel(
      
      helpText("This is a slider"),
      
      # Input: Slider for the number of bins ----
      sliderInput(inputId = "bins",
                  label = "Number of bins:",
                  min = 1,
                  max = 50,
                  value = 30),
      
      img(src = "chinstrap.jpg", height = 200, width = 300),
      
      helpText("Select y-axis scatter plot variable"),
      
      selectInput(inputId = "select", 
                  label = "Penguin Variables:", 
                  choices = c("Bill Length (mm)" = "bill_length_mm", 
                              "Bill Depth (mm)" = "bill_depth_mm",
                              "Flipper Length (mm)" = "flipper_length_mm"))
      ),
    
    # Main panel for displaying outputs ----
    mainPanel(
      
      p("This data is for Adelie, Chinstrap and Gentoo penguins from the Palmer Archipelago."),
      # Output: Histogram ----
      plotOutput(outputId = "distPlot"),
      
      p("This histogram shows the distribution of penguins' flipper lengths (in mm)."),
      
      plotOutput(outputId = "scatterPlot"),
      
     p("This scatter plot models the penguins' body masses (in grams) against one of a selection of other variables."),
      
    )
  )
)

# Define server logic required to draw a histogram ----
server <- function(input, output) {
  
  output$distPlot <- 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")
  })
  
output$scatterPlot <- renderPlot({

  w <- penguins[, c("body_mass_g", input$select), drop = FALSE]
  
  plot(w, col = "#75AADB")
  
})
  
}

shinyApp(ui = ui, server = server)

7 Publishing your Shiny App

Please refer to Section 6 of the Data Visualisation in R supplement.


That’s everything covered, well done.


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.

LS0tDQp0aXRsZTogIlNUTTEwMDE6IENvbXB1dGVyIExhYiA0QiBTb2x1dGlvbnMiDQpvdXRwdXQ6DQogIGJvb2tkb3duOjpodG1sX2RvY3VtZW50MjogDQogICAgdG9jOiB0cnVlDQogICAgdG9jX2Zsb2F0OiB0cnVlDQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KICAgIHRoZW1lOiByZWFkYWJsZQ0KICAgIGNvZGVfZm9sZGluZzogc2hvdw0KYmlibGlvZ3JhcGh5OiBTVE0xMDAxX0RTX0NMX3JlZmVyZW5jZXMuYmliIA0KbGluay1jaXRhdGlvbnM6IHllcw0KLS0tDQoNCjxzdHlsZT4NCiNUT0Mgew0KICBiYWNrZ3JvdW5kOiB1cmwoImh0dHBzOi8vd3d3LmxhdHJvYmUuZWR1LmF1L19tZWRpYS9sYS10cm9iZS1hcGkvdjUvaW1nL2xvZ28uc3ZnIik7DQogIGJhY2tncm91bmQtc2l6ZTogY29udGFpbjsNCiAgcGFkZGluZy10b3A6IDgwcHggIWltcG9ydGFudDsNCiAgYmFja2dyb3VuZC1yZXBlYXQ6IG5vLXJlcGVhdDsNCn0NCjwvc3R5bGU+DQoNCiMjIyBEYXRhIFNjaWVuY2UgTW9kdWxlIHstfQ0KDQojIyMgVG9waWMgNEI6IERhdGEgVmlzdWFsaXNhdGlvbiBJViB7LX0NCg0KPGJyPg0KDQpFeGFtcGxlIFIgY29kZSBzb2x1dGlvbnMgZm9yIHRoZSBbRGF0YSBTY2llbmNlIENvbXB1dGVyIExhYiA0XShodHRwczovL3JwdWJzLmNvbS9MVFVfU1RNMTAwMS9EU01DTDRfUyksIHdoaWNoIHVzZXMgZGF0YSBmcm9tIHRoZSBgcGFsbWVycGVuZ3VpbnNgIFIgcGFja2FnZSBbQHBlbmd1aW5zXSwgYW5kIHRoZSBgc2hpbnlgIFIgcGFja2FnZSBbQHNoaW55XSwgYXJlIHByZXNlbnRlZCBiZWxvdy4NCg0KVGhpcyBjb21wdXRlciBsYWIgd2FzIGRlc2lnbmVkIHRvIHJ1biBhbG9uZ3NpZGUgdGhlIGNvbnRlbnQgaW4gW1NlY3Rpb24gNiBvZiB0aGUgRGF0YSBWaXN1YWxpc2F0aW9uIGluIFIgc3VwcGxlbWVudF0oaHR0cHM6Ly9ib29rZG93bi5vcmcvcmVoay9zdG0xMDAxX2RzbV90MV9kYXRhX3Zpc3VhbGlzYXRpb25faW5fci9jcmVhdGluZy13ZWItYXBwcy11c2luZy1zaGlueS1jb21wdXRlci1sYWItNGIuaHRtbCkuIEl0IG1pZ2h0IGJlIGhlbHBmdWwgdG8gaGF2ZSB0aGlzIGNvbnRlbnQgb3BlbiB3aGlsZSB5b3UgbG9vayB0aHJvdWdoIHRoZXNlIHNvbHV0aW9ucy4NCg0KIyBQcmVwYXJhdGlvbnMNCg0KIyMNCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBGLCBlY2hvID0gVH0NCmluc3RhbGwucGFja2FnZXMoInNoaW55IikNCmxpYnJhcnkoc2hpbnkpDQpgYGANCg0KIyMNCg0KSW4gdGhpcyBzdGVwLCB5b3UgbmVlZGVkIHRvIGNyZWF0ZSBhIG5ldyBmb2xkZXIgY2FsbGVkIGBzaGlueV9hcHBfdGVzdGAgd2l0aGluIHlvdXIgY3VycmVudCB3b3JraW5nIGRpcmVjdG9yeS4NCg0KRm9yIGV4YW1wbGUsIGlmIHlvdXIgY3VycmVudCB3b3JraW5nIGRpcmVjdG9yeSB3YXMgDQpgIkM6L1VzZXJzL0plcnJ5L0Rlc2t0b3AvU1RNMTAwMSJgDQp5b3VyIFNoaW55IGFwcCBkaXJlY3Rvcnkgd291bGQgYmUgDQpgIkM6L1VzZXJzL0plcnJ5L0Rlc2t0b3AvU1RNMTAwMS9zaGlueV9hcHBfdGVzdCJgLg0KDQojIyB7I2Jhc2Vjb2RlfQ0KDQpUaGUgUiBjb2RlIGJlbG93IHNob3VsZCBoYXZlIGJlZW4gc2F2ZWQgaW4gYSBzY3JpcHQgbmFtZWQgYGFwcC5SYCB3aXRoaW4geW91ciBuZXcgZm9sZGVyIGBzaGlueV9hcHBfdGVzdGAuDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUiLCBldmFsID0gRiwgZWNobyA9IFR9DQpsaWJyYXJ5KHNoaW55KQ0KDQojIERlZmluZSBVSSBmb3IgYXBwIHRoYXQgZHJhd3MgYSBoaXN0b2dyYW0gLS0tLQ0KdWkgPC0gZmx1aWRQYWdlKA0KDQogICMgQXBwIHRpdGxlIC0tLS0NCiAgdGl0bGVQYW5lbCgiSGVsbG8gU2hpbnkhIiksDQoNCiAgIyBTaWRlYmFyIGxheW91dCB3aXRoIGlucHV0IGFuZCBvdXRwdXQgZGVmaW5pdGlvbnMgLS0tLQ0KICBzaWRlYmFyTGF5b3V0KA0KDQogICAgIyBTaWRlYmFyIHBhbmVsIGZvciBpbnB1dHMgLS0tLQ0KICAgIHNpZGViYXJQYW5lbCgNCg0KICAgICAgIyBJbnB1dDogU2xpZGVyIGZvciB0aGUgbnVtYmVyIG9mIGJpbnMgLS0tLQ0KICAgICAgc2xpZGVySW5wdXQoaW5wdXRJZCA9ICJiaW5zIiwNCiAgICAgICAgICAgICAgICAgIGxhYmVsID0gIk51bWJlciBvZiBiaW5zOiIsDQogICAgICAgICAgICAgICAgICBtaW4gPSAxLA0KICAgICAgICAgICAgICAgICAgbWF4ID0gNTAsDQogICAgICAgICAgICAgICAgICB2YWx1ZSA9IDMwKQ0KDQogICAgKSwNCg0KICAgICMgTWFpbiBwYW5lbCBmb3IgZGlzcGxheWluZyBvdXRwdXRzIC0tLS0NCiAgICBtYWluUGFuZWwoDQoNCiAgICAgICMgT3V0cHV0OiBIaXN0b2dyYW0gLS0tLQ0KICAgICAgcGxvdE91dHB1dChvdXRwdXRJZCA9ICJkaXN0UGxvdCIpDQoNCiAgICApDQogICkNCikNCg0KIyBEZWZpbmUgc2VydmVyIGxvZ2ljIHJlcXVpcmVkIHRvIGRyYXcgYSBoaXN0b2dyYW0gLS0tLQ0Kc2VydmVyIDwtIGZ1bmN0aW9uKGlucHV0LCBvdXRwdXQpIHsNCg0KICAjIEhpc3RvZ3JhbSBvZiB0aGUgT2xkIEZhaXRoZnVsIEdleXNlciBEYXRhIC0tLS0NCiAgIyB3aXRoIHJlcXVlc3RlZCBudW1iZXIgb2YgYmlucw0KICAjIFRoaXMgZXhwcmVzc2lvbiB0aGF0IGdlbmVyYXRlcyBhIGhpc3RvZ3JhbSBpcyB3cmFwcGVkIGluIGEgY2FsbA0KICAjIHRvIHJlbmRlclBsb3QgdG8gaW5kaWNhdGUgdGhhdDoNCiAgIw0KICAjIDEuIEl0IGlzICJyZWFjdGl2ZSIgYW5kIHRoZXJlZm9yZSBzaG91bGQgYmUgYXV0b21hdGljYWxseQ0KICAjICAgIHJlLWV4ZWN1dGVkIHdoZW4gaW5wdXRzIChpbnB1dCRiaW5zKSBjaGFuZ2UNCiAgIyAyLiBJdHMgb3V0cHV0IHR5cGUgaXMgYSBwbG90DQogIG91dHB1dCRkaXN0UGxvdCA8LSByZW5kZXJQbG90KHsNCg0KICAgIHggICAgPC0gZmFpdGhmdWwkd2FpdGluZw0KICAgIGJpbnMgPC0gc2VxKG1pbih4KSwgbWF4KHgpLCBsZW5ndGgub3V0ID0gaW5wdXQkYmlucyArIDEpDQoNCiAgICBoaXN0KHgsIGJyZWFrcyA9IGJpbnMsIGNvbCA9ICIjNzVBQURCIiwgYm9yZGVyID0gIndoaXRlIiwNCiAgICAgICAgIHhsYWIgPSAiV2FpdGluZyB0aW1lIHRvIG5leHQgZXJ1cHRpb24gKGluIG1pbnMpIiwNCiAgICAgICAgIG1haW4gPSAiSGlzdG9ncmFtIG9mIHdhaXRpbmcgdGltZXMiKQ0KDQogICAgfSkNCg0KfQ0KDQpzaGlueUFwcCh1aSA9IHVpLCBzZXJ2ZXIgPSBzZXJ2ZXIpDQpgYGANCg0KIyMNCg0KUnVubmluZyB0aGUgY29kZSBiZWxvdyBpbiBhIG5ldyBSIHNjcmlwdCBmaWxlIHNob3VsZCBoYXZlIGxlZCB0byB0aGUgYXBwZWFyYW5jZSBvZiB0aGUgYEhlbGxvIFNoaW55IWAgZXhhbXBsZSBTaGlueSBhcHAgZGlzY3Vzc2VkIGluIFtTZWN0aW9uIDYgb2YgdGhlIERhdGEgVmlzdWFsaXNhdGlvbiBpbiBSIHN1cHBsZW1lbnRdKGh0dHBzOi8vYm9va2Rvd24ub3JnL3JlaGsvc3RtMTAwMV9kc21fdDFfZGF0YV92aXN1YWxpc2F0aW9uX2luX3IvY3JlYXRpbmctd2ViLWFwcHMtdXNpbmctc2hpbnktY29tcHV0ZXItbGFiLTRiLmh0bWwpLg0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IEYsIGVjaG8gPSBUfQ0KcnVuQXBwKCJhcHAuUiIpDQpgYGANCg0KIyBNb2RpZnlpbmcgYW4gZXhpc3RpbmcgU2hpbnkgYXBwDQoNCiMjDQoNClRoZSBsaW5lIG9mIGNvZGUNCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBGLCBlY2hvID0gVH0NCiAgdGl0bGVQYW5lbCgiSGVsbG8gU2hpbnkhIiksDQpgYGANCg0Kc2hvdWxkIGhhdmUgYmVlbiBjaGFuZ2VkIHRvDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gRiwgZWNobyA9IFR9DQogIHRpdGxlUGFuZWwoIkhlbGxvIERhdGEgU2NpZW5jZSIpLA0KYGBgDQoNCiMjDQoNClRoZSBjb2RlIGNodW5rDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gRiwgZWNobyA9IFR9DQogc2xpZGVySW5wdXQoaW5wdXRJZCA9ICJiaW5zIiwNCiAgICAgICAgICAgICAgICAgIGxhYmVsID0gIk51bWJlciBvZiBiaW5zOiIsDQogICAgICAgICAgICAgICAgICBtaW4gPSAxLA0KICAgICAgICAgICAgICAgICAgbWF4ID0gNTAsDQogICAgICAgICAgICAgICAgICB2YWx1ZSA9IDMwKQ0KYGBgDQoNCnNob3VsZCBoYXZlIGJlZW4gY2hhbmdlZCB0byANCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBGLCBlY2hvID0gVH0NCiBzbGlkZXJJbnB1dChpbnB1dElkID0gImJpbnMiLA0KICAgICAgICAgICAgICAgICAgbGFiZWwgPSAiTnVtYmVyIG9mIGJpbnM6IiwNCiAgICAgICAgICAgICAgICAgIG1pbiA9IDMsDQogICAgICAgICAgICAgICAgICBtYXggPSA0MCwNCiAgICAgICAgICAgICAgICAgIHZhbHVlID0gMjApDQpgYGANCg0KIyMNCg0KVGhlIGNvZGUgY2h1bmsNCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBGLCBlY2hvID0gVH0NCiAgaGlzdCh4LCBicmVha3MgPSBiaW5zLCBjb2wgPSAiIzc1QUFEQiIsIGJvcmRlciA9ICJ3aGl0ZSIsDQogICAgICAgICB4bGFiID0gIldhaXRpbmcgdGltZSB0byBuZXh0IGVydXB0aW9uIChpbiBtaW5zKSIsDQogICAgICAgICBtYWluID0gIkhpc3RvZ3JhbSBvZiB3YWl0aW5nIHRpbWVzIikNCmBgYA0KDQpzaG91bGQgaGF2ZSBiZWVuIGNoYW5nZWQsIGZvciBleGFtcGxlLCB0bw0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IEYsIGVjaG8gPSBUfQ0KICBoaXN0KHgsIGJyZWFrcyA9IGJpbnMsIGNvbCA9ICJyZWQiLCBib3JkZXIgPSAid2hpdGUiLA0KICAgICAgICAgeGxhYiA9ICJXYWl0aW5nIHRpbWUgdG8gbmV4dCBlcnVwdGlvbiAoaW4gbWlucykiLA0KICAgICAgICAgbWFpbiA9ICJIaXN0b2dyYW0gb2Ygd2FpdGluZyB0aW1lcyIpDQpgYGANCg0KIyMgDQoNCldpdGhpbiB0aGUgYHVpYCBvYmplY3QsIHRoZSBsaW5lIG9mIGNvZGUNCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBGLCBlY2hvID0gVH0NCiAgICAgIHBsb3RPdXRwdXQob3V0cHV0SWQgPSAiZGlzdFBsb3QiKQ0KYGBgDQogICAgICANCnNob3VsZCBoYXZlIGJlZW4gY2hhbmdlZCB0bw0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IEYsIGVjaG8gPSBUfQ0KcGxvdE91dHB1dChvdXRwdXRJZCA9ICJkaXNwbGF5SGlzdCIpDQpgYGANCg0KYW5kIHdpdGhpbiB0aGUgYHNlcnZlcmAgZnVuY3Rpb24sIHRoZSBsaW5lIG9mIGNvZGUNCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBGLCBlY2hvID0gVH0NCiAgb3V0cHV0JGRpc3RQbG90IDwtIHJlbmRlclBsb3Qoew0KYGBgDQoNCnNob3VsZCBoYXZlIGJlZW4gY2hhbmdlZCB0byANCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBGLCBlY2hvID0gVH0NCiAgb3V0cHV0JGRpc3BsYXlIaXN0IDwtIHJlbmRlclBsb3Qoew0KYGBgDQoNCiMjDQoNCk5vIGFuc3dlciByZXF1aXJlZC4gSWYgYWxsIHdlbnQgd2VsbCwgeW91ciBtb2RpZmllZCBhcHAgc2hvdWxkIGhhdmUgYXBwZWFyZWQuDQoNCiMgTWFraW5nIGEgU2hpbnkgYXBwIGZvciB0aGUgUGVuZ3VpbnMgZGF0YQ0KDQojIw0KDQpUaGUgZnVsbCBjb2RlIGZvciB0aGUgcmVxdWVzdGVkIGBhcHAuUmAgc2NyaXB0IGlzIHByb3ZpZGVkIGJlbG93Lg0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIiwgZXZhbCA9IEYsIGVjaG8gPSBUfQ0KbGlicmFyeShzaGlueSkNCmxpYnJhcnkocGFsbWVycGVuZ3VpbnMpDQpwZW5ndWlucyA8LSBuYS5vbWl0KHBlbmd1aW5zKQ0KDQojIERlZmluZSBVSSBmb3IgYXBwIHRoYXQgZHJhd3MgYSBoaXN0b2dyYW0gLS0tLQ0KdWkgPC0gZmx1aWRQYWdlKA0KICANCiAgIyBBcHAgdGl0bGUgLS0tLQ0KICB0aXRsZVBhbmVsKCJQYWxtZXIgUGVuZ3VpbnMgRmxpcHBlciBMZW5ndGgiKSwNCiAgDQogICMgU2lkZWJhciBsYXlvdXQgd2l0aCBpbnB1dCBhbmQgb3V0cHV0IGRlZmluaXRpb25zIC0tLS0NCiAgc2lkZWJhckxheW91dCgNCiAgICANCiAgICAjIFNpZGViYXIgcGFuZWwgZm9yIGlucHV0cyAtLS0tDQogICAgc2lkZWJhclBhbmVsKA0KDQogICAgICAjIElucHV0OiBTbGlkZXIgZm9yIHRoZSBudW1iZXIgb2YgYmlucyAtLS0tDQogICAgICBzbGlkZXJJbnB1dChpbnB1dElkID0gImJpbnMiLA0KICAgICAgICAgICAgICAgICAgbGFiZWwgPSAiTnVtYmVyIG9mIGJpbnM6IiwNCiAgICAgICAgICAgICAgICAgIG1pbiA9IDEsDQogICAgICAgICAgICAgICAgICBtYXggPSA1MCwNCiAgICAgICAgICAgICAgICAgIHZhbHVlID0gMzApLA0KICAgICAgKSwNCiAgICANCiAgICAjIE1haW4gcGFuZWwgZm9yIGRpc3BsYXlpbmcgb3V0cHV0cyAtLS0tDQogICAgbWFpblBhbmVsKA0KICAgIA0KICAgICAgIyBPdXRwdXQ6IEhpc3RvZ3JhbSAtLS0tDQogICAgICBwbG90T3V0cHV0KG91dHB1dElkID0gImRpc3RQbG90IiksDQogICAgKQ0KICApDQopDQoNCiMgRGVmaW5lIHNlcnZlciBsb2dpYyByZXF1aXJlZCB0byBkcmF3IGEgaGlzdG9ncmFtIC0tLS0NCnNlcnZlciA8LSBmdW5jdGlvbihpbnB1dCwgb3V0cHV0KSB7DQogIA0KICBvdXRwdXQkZGlzdFBsb3QgPC0gcmVuZGVyUGxvdCh7DQogICAgDQogICAgeCAgICA8LSBwZW5ndWlucyRmbGlwcGVyX2xlbmd0aF9tbQ0KICAgIGJpbnMgPC0gc2VxKG1pbih4KSwgbWF4KHgpLCBsZW5ndGgub3V0ID0gaW5wdXQkYmlucyArIDEpDQogICAgDQogICAgaGlzdCh4LCBicmVha3MgPSBiaW5zLCBjb2wgPSAiIzc1QUFEQiIsIGJvcmRlciA9ICJ3aGl0ZSIsDQogICAgICAgICB4bGFiID0gIkZsaXBwZXIgbGVuZ3RoIG9mIHBlbmd1aW5zIChtbSkiLA0KICAgICAgICAgbWFpbiA9ICJIaXN0b2dyYW0gb2YgUGVuZ3VpbiBGbGlwcGVyIExlbmd0aHMiKQ0KICB9KQ0KICANCn0NCg0Kc2hpbnlBcHAodWkgPSB1aSwgc2VydmVyID0gc2VydmVyKQ0KYGBgDQoNCiMjIHsjYmFzZWltYWdlfQ0KDQpObyBhbnN3ZXIgcmVxdWlyZWQuIE5vdGUgeW91IGNhbiBydW4gdGhlIGNvZGUgcHJvdmlkZWQgYWJvdmUgKG9uY2Ugc2F2ZWQgaW4gYW4gYXBwcm9wcmlhdGUgZGlyZWN0b3J5KSB0byBnZW5lcmF0ZSB0aGUgcmVxdWVzdGVkIGFwcC4NCg0KIyBBZGRpbmcgd2lkZ2V0cyB0byBhIFNoaW55IGFwcA0KDQojIw0KDQpObyBhbnN3ZXIgcmVxdWlyZWQuDQoNCiMjDQoNCk5vIGFuc3dlciByZXF1aXJlZC4NCg0KIyMgeyNiZWdpbm5pbmd9DQoNCkFuIGV4YW1wbGUgb2YgdGhlIGNvbXBsZXRlZCBjb2RlIGNodW5rIGlzDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gRiwgZWNobyA9IFR9DQogICAgICBzZWxlY3RJbnB1dChpbnB1dElkID0gInNlbGVjdCIsIA0KICAgICAgICAgICAgICAgICAgbGFiZWwgPSAiUGVuZ3VpbiBWYXJpYWJsZXM6IiwgDQogICAgICAgICAgICAgICAgICBjaG9pY2VzID0gYygiQmlsbCBMZW5ndGggKG1tKSIgPSAiYmlsbF9sZW5ndGhfbW0iLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJCaWxsIERlcHRoIChtbSkiID0gImJpbGxfZGVwdGhfbW0iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkZsaXBwZXIgTGVuZ3RoIChtbSkiID0gImZsaXBwZXJfbGVuZ3RoX21tIikpLA0KYGBgDQoNCmFuZCBvbmNlIGl0IGlzIHBsYWNlZCBpbiB0aGUgYXBwcm9wcmlhdGUgcG9zaXRpb24gd2l0aCB0aGUgYGFwcC5SYCBzY3JpcHQsIHRoaXMgc2NyaXB0IHNob3VsZCBsb29rIGxpa2UgdGhlIGNvZGUgc2hvd24gYmVsb3c6DQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUiLCBldmFsID0gRiwgZWNobyA9IFR9DQpsaWJyYXJ5KHNoaW55KQ0KbGlicmFyeShwYWxtZXJwZW5ndWlucykNCnBlbmd1aW5zIDwtIG5hLm9taXQocGVuZ3VpbnMpDQoNCiMgRGVmaW5lIFVJIGZvciBhcHAgdGhhdCBkcmF3cyBhIGhpc3RvZ3JhbSAtLS0tDQp1aSA8LSBmbHVpZFBhZ2UoDQogIA0KICAjIEFwcCB0aXRsZSAtLS0tDQogIHRpdGxlUGFuZWwoIlBhbG1lciBQZW5ndWlucyBGbGlwcGVyIExlbmd0aCIpLA0KICANCiAgIyBTaWRlYmFyIGxheW91dCB3aXRoIGlucHV0IGFuZCBvdXRwdXQgZGVmaW5pdGlvbnMgLS0tLQ0KICBzaWRlYmFyTGF5b3V0KA0KICAgIA0KICAgICMgU2lkZWJhciBwYW5lbCBmb3IgaW5wdXRzIC0tLS0NCiAgICBzaWRlYmFyUGFuZWwoDQogICAgICANCiAgICAgIHNlbGVjdElucHV0KGlucHV0SWQgPSAic2VsZWN0IiwgDQogICAgICAgICAgICAgICAgICBsYWJlbCA9ICJQZW5ndWluIFZhcmlhYmxlczoiLCANCiAgICAgICAgICAgICAgICAgIGNob2ljZXMgPSBjKCJCaWxsIExlbmd0aCAobW0pIiA9ICJiaWxsX2xlbmd0aF9tbSIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkJpbGwgRGVwdGggKG1tKSIgPSAiYmlsbF9kZXB0aF9tbSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRmxpcHBlciBMZW5ndGggKG1tKSIgPSAiZmxpcHBlcl9sZW5ndGhfbW0iKSksDQogICAgICANCiAgICAgICMgSW5wdXQ6IFNsaWRlciBmb3IgdGhlIG51bWJlciBvZiBiaW5zIC0tLS0NCiAgICAgIHNsaWRlcklucHV0KGlucHV0SWQgPSAiYmlucyIsDQogICAgICAgICAgICAgICAgICBsYWJlbCA9ICJOdW1iZXIgb2YgYmluczoiLA0KICAgICAgICAgICAgICAgICAgbWluID0gMSwNCiAgICAgICAgICAgICAgICAgIG1heCA9IDUwLA0KICAgICAgICAgICAgICAgICAgdmFsdWUgPSAzMCksDQogICAgICApLA0KICAgIA0KICAgICMgTWFpbiBwYW5lbCBmb3IgZGlzcGxheWluZyBvdXRwdXRzIC0tLS0NCiAgICBtYWluUGFuZWwoDQogICAgDQogICAgICAjIE91dHB1dDogSGlzdG9ncmFtIC0tLS0NCiAgICAgIHBsb3RPdXRwdXQob3V0cHV0SWQgPSAiZGlzdFBsb3QiKSwNCiAgICApDQogICkNCikNCg0KIyBEZWZpbmUgc2VydmVyIGxvZ2ljIHJlcXVpcmVkIHRvIGRyYXcgYSBoaXN0b2dyYW0gLS0tLQ0Kc2VydmVyIDwtIGZ1bmN0aW9uKGlucHV0LCBvdXRwdXQpIHsNCiAgDQogIG91dHB1dCRkaXN0UGxvdCA8LSByZW5kZXJQbG90KHsNCiAgICANCiAgICB4ICAgIDwtIHBlbmd1aW5zJGZsaXBwZXJfbGVuZ3RoX21tDQogICAgYmlucyA8LSBzZXEobWluKHgpLCBtYXgoeCksIGxlbmd0aC5vdXQgPSBpbnB1dCRiaW5zICsgMSkNCiAgICANCiAgICBoaXN0KHgsIGJyZWFrcyA9IGJpbnMsIGNvbCA9ICIjNzVBQURCIiwgYm9yZGVyID0gIndoaXRlIiwNCiAgICAgICAgIHhsYWIgPSAiRmxpcHBlciBsZW5ndGggb2YgcGVuZ3VpbnMgKG1tKSIsDQogICAgICAgICBtYWluID0gIkhpc3RvZ3JhbSBvZiBQZW5ndWluIEZsaXBwZXIgTGVuZ3RocyIpDQogIH0pDQogIA0KfQ0KDQpzaGlueUFwcCh1aSA9IHVpLCBzZXJ2ZXIgPSBzZXJ2ZXIpDQpgYGANCg0KIyMNCg0KTm8gYW5zd2VyIHJlcXVpcmVkLg0KDQojIE1ha2luZyBSZWFjdGl2ZSBPdXRwdXRzIGZvciBTaGlueQ0KDQojIyB7I2Rpc3RwbG90Y29tbWFuZH0NCg0KT25jZSBhZGRlZCwgeW91ciBgYXBwLlJgIHNjcmlwdCBzaG91bGQgbG9vayBsaWtlIHRoaXM6DQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUiLCBldmFsID0gRiwgZWNobyA9IFR9DQpsaWJyYXJ5KHNoaW55KQ0KbGlicmFyeShwYWxtZXJwZW5ndWlucykNCnBlbmd1aW5zIDwtIG5hLm9taXQocGVuZ3VpbnMpDQoNCiMgRGVmaW5lIFVJIGZvciBhcHAgdGhhdCBkcmF3cyBhIGhpc3RvZ3JhbSAtLS0tDQp1aSA8LSBmbHVpZFBhZ2UoDQogIA0KICAjIEFwcCB0aXRsZSAtLS0tDQogIHRpdGxlUGFuZWwoIlBhbG1lciBQZW5ndWlucyBGbGlwcGVyIExlbmd0aCIpLA0KICANCiAgIyBTaWRlYmFyIGxheW91dCB3aXRoIGlucHV0IGFuZCBvdXRwdXQgZGVmaW5pdGlvbnMgLS0tLQ0KICBzaWRlYmFyTGF5b3V0KA0KICAgIA0KICAgICMgU2lkZWJhciBwYW5lbCBmb3IgaW5wdXRzIC0tLS0NCiAgICBzaWRlYmFyUGFuZWwoDQogICAgICANCiAgICAgIHNlbGVjdElucHV0KGlucHV0SWQgPSAic2VsZWN0IiwgDQogICAgICAgICAgICAgICAgICBsYWJlbCA9ICJQZW5ndWluIFZhcmlhYmxlczoiLCANCiAgICAgICAgICAgICAgICAgIGNob2ljZXMgPSBjKCJCaWxsIExlbmd0aCAobW0pIiA9ICJiaWxsX2xlbmd0aF9tbSIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkJpbGwgRGVwdGggKG1tKSIgPSAiYmlsbF9kZXB0aF9tbSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRmxpcHBlciBMZW5ndGggKG1tKSIgPSAiZmxpcHBlcl9sZW5ndGhfbW0iKSksDQogICAgICANCiAgICAgICMgSW5wdXQ6IFNsaWRlciBmb3IgdGhlIG51bWJlciBvZiBiaW5zIC0tLS0NCiAgICAgIHNsaWRlcklucHV0KGlucHV0SWQgPSAiYmlucyIsDQogICAgICAgICAgICAgICAgICBsYWJlbCA9ICJOdW1iZXIgb2YgYmluczoiLA0KICAgICAgICAgICAgICAgICAgbWluID0gMSwNCiAgICAgICAgICAgICAgICAgIG1heCA9IDUwLA0KICAgICAgICAgICAgICAgICAgdmFsdWUgPSAzMCksDQogICAgICApLA0KICAgIA0KICAgICMgTWFpbiBwYW5lbCBmb3IgZGlzcGxheWluZyBvdXRwdXRzIC0tLS0NCiAgICBtYWluUGFuZWwoDQogICAgDQogICAgICAjIE91dHB1dDogSGlzdG9ncmFtIC0tLS0NCiAgICAgIHBsb3RPdXRwdXQob3V0cHV0SWQgPSAiZGlzdFBsb3QiKSwNCiAgICAgIA0KICAgICAgcGxvdE91dHB1dChvdXRwdXRJZCA9ICJzY2F0dGVyUGxvdCIpDQogICAgKQ0KICApDQopDQoNCiMgRGVmaW5lIHNlcnZlciBsb2dpYyByZXF1aXJlZCB0byBkcmF3IGEgaGlzdG9ncmFtIC0tLS0NCnNlcnZlciA8LSBmdW5jdGlvbihpbnB1dCwgb3V0cHV0KSB7DQogIA0KICBvdXRwdXQkZGlzdFBsb3QgPC0gcmVuZGVyUGxvdCh7DQogICAgDQogICAgeCAgICA8LSBwZW5ndWlucyRmbGlwcGVyX2xlbmd0aF9tbQ0KICAgIGJpbnMgPC0gc2VxKG1pbih4KSwgbWF4KHgpLCBsZW5ndGgub3V0ID0gaW5wdXQkYmlucyArIDEpDQogICAgDQogICAgaGlzdCh4LCBicmVha3MgPSBiaW5zLCBjb2wgPSAiIzc1QUFEQiIsIGJvcmRlciA9ICJ3aGl0ZSIsDQogICAgICAgICB4bGFiID0gIkZsaXBwZXIgbGVuZ3RoIG9mIHBlbmd1aW5zIChtbSkiLA0KICAgICAgICAgbWFpbiA9ICJIaXN0b2dyYW0gb2YgUGVuZ3VpbiBGbGlwcGVyIExlbmd0aHMiKQ0KICB9KQ0KICANCn0NCg0Kc2hpbnlBcHAodWkgPSB1aSwgc2VydmVyID0gc2VydmVyKQ0KYGBgDQoNCiMjDQoNCk9uY2UgeW91IGhhdmUgYWRkZWQgdGhlIGNvZGUsIHlvdXIgYGFwcC5SYCBzY3JpcHQgc2hvdWxkIGxvb2sgbGlrZSB0aGlzOg0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIiwgZXZhbCA9IEYsIGVjaG8gPSBUfQ0KbGlicmFyeShzaGlueSkNCmxpYnJhcnkocGFsbWVycGVuZ3VpbnMpDQpwZW5ndWlucyA8LSBuYS5vbWl0KHBlbmd1aW5zKQ0KDQojIERlZmluZSBVSSBmb3IgYXBwIHRoYXQgZHJhd3MgYSBoaXN0b2dyYW0gLS0tLQ0KdWkgPC0gZmx1aWRQYWdlKA0KICANCiAgIyBBcHAgdGl0bGUgLS0tLQ0KICB0aXRsZVBhbmVsKCJQYWxtZXIgUGVuZ3VpbnMgRmxpcHBlciBMZW5ndGgiKSwNCiAgDQogICMgU2lkZWJhciBsYXlvdXQgd2l0aCBpbnB1dCBhbmQgb3V0cHV0IGRlZmluaXRpb25zIC0tLS0NCiAgc2lkZWJhckxheW91dCgNCiAgICANCiAgICAjIFNpZGViYXIgcGFuZWwgZm9yIGlucHV0cyAtLS0tDQogICAgc2lkZWJhclBhbmVsKA0KICAgICAgDQogICAgICBzZWxlY3RJbnB1dChpbnB1dElkID0gInNlbGVjdCIsIA0KICAgICAgICAgICAgICAgICAgbGFiZWwgPSAiUGVuZ3VpbiBWYXJpYWJsZXM6IiwgDQogICAgICAgICAgICAgICAgICBjaG9pY2VzID0gYygiQmlsbCBMZW5ndGggKG1tKSIgPSAiYmlsbF9sZW5ndGhfbW0iLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJCaWxsIERlcHRoIChtbSkiID0gImJpbGxfZGVwdGhfbW0iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkZsaXBwZXIgTGVuZ3RoIChtbSkiID0gImZsaXBwZXJfbGVuZ3RoX21tIikpLA0KICAgICAgDQogICAgICAjIElucHV0OiBTbGlkZXIgZm9yIHRoZSBudW1iZXIgb2YgYmlucyAtLS0tDQogICAgICBzbGlkZXJJbnB1dChpbnB1dElkID0gImJpbnMiLA0KICAgICAgICAgICAgICAgICAgbGFiZWwgPSAiTnVtYmVyIG9mIGJpbnM6IiwNCiAgICAgICAgICAgICAgICAgIG1pbiA9IDEsDQogICAgICAgICAgICAgICAgICBtYXggPSA1MCwNCiAgICAgICAgICAgICAgICAgIHZhbHVlID0gMzApLA0KICAgICAgKSwNCiAgICANCiAgICAjIE1haW4gcGFuZWwgZm9yIGRpc3BsYXlpbmcgb3V0cHV0cyAtLS0tDQogICAgbWFpblBhbmVsKA0KICAgIA0KICAgICAgIyBPdXRwdXQ6IEhpc3RvZ3JhbSAtLS0tDQogICAgICBwbG90T3V0cHV0KG91dHB1dElkID0gImRpc3RQbG90IiksDQogICAgICANCiAgICAgIHBsb3RPdXRwdXQob3V0cHV0SWQgPSAic2NhdHRlclBsb3QiKQ0KICAgICAgDQogICAgKQ0KICApDQopDQoNCiMgRGVmaW5lIHNlcnZlciBsb2dpYyByZXF1aXJlZCB0byBkcmF3IGEgaGlzdG9ncmFtIC0tLS0NCnNlcnZlciA8LSBmdW5jdGlvbihpbnB1dCwgb3V0cHV0KSB7DQogIA0KICBvdXRwdXQkZGlzdFBsb3QgPC0gcmVuZGVyUGxvdCh7DQogICAgDQogICAgeCAgICA8LSBwZW5ndWlucyRmbGlwcGVyX2xlbmd0aF9tbQ0KICAgIGJpbnMgPC0gc2VxKG1pbih4KSwgbWF4KHgpLCBsZW5ndGgub3V0ID0gaW5wdXQkYmlucyArIDEpDQogICAgDQogICAgaGlzdCh4LCBicmVha3MgPSBiaW5zLCBjb2wgPSAiIzc1QUFEQiIsIGJvcmRlciA9ICJ3aGl0ZSIsDQogICAgICAgICB4bGFiID0gIkZsaXBwZXIgbGVuZ3RoIG9mIHBlbmd1aW5zIChtbSkiLA0KICAgICAgICAgbWFpbiA9ICJIaXN0b2dyYW0gb2YgUGVuZ3VpbiBGbGlwcGVyIExlbmd0aHMiKQ0KICAgIA0KICB9KQ0KICANCiAgb3V0cHV0JHNjYXR0ZXJQbG90IDwtIHJlbmRlclBsb3Qoew0KICAgIA0KICAgIHcgPC0gcGVuZ3VpbnNbLCBjKCJib2R5X21hc3NfZyIsIGlucHV0JHNlbGVjdCksIGRyb3AgPSBGQUxTRV0NCiAgICANCiAgICBwbG90KHcsIGNvbCA9ICIjNzVBQURCIikNCiAgICANCiAgfSkNCiAgDQp9DQoNCnNoaW55QXBwKHVpID0gdWksIHNlcnZlciA9IHNlcnZlcikNCmBgYA0KDQojIw0KDQpObyBhbnN3ZXIgcmVxdWlyZWQuDQoNCiMgTW9kaWZ5aW5nIHRoZSBTaGlueSBVc2VyIEludGVyZmFjZQ0KDQojIw0KDQpUbyBjaGFuZ2UgdGhlIHNpZGViYXIgcG9zaXRpb24sIHRoZSBsaW5lIG9mIGNvZGUgDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gRiwgZWNobyA9IFR9DQogIHNpZGViYXJMYXlvdXQoDQpgYGANCiAgDQpzaG91bGQgbm93IGxvb2sgbGlrZSB0aGlzOg0KICANCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gRiwgZWNobyA9IFR9DQogICAgc2lkZWJhckxheW91dChwb3NpdGlvbiA9ICJyaWdodCIsDQpgYGANCg0KIyMgeyNzaWRlYmFyfQ0KDQpUaGUgYHNpZGViYXJQYW5lbGAgc2VjdGlvbiBvZiBjb2RlIGluIHlvdXIgYHVpYCBvYmplY3Qgc2hvdWxkIG5vdyBsb29rIGxpa2UgdGhpczoNCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBGLCBlY2hvID0gVH0NCiAgICBzaWRlYmFyUGFuZWwoDQogICAgICANCiAgICAgICAgICAgICMgSW5wdXQ6IFNsaWRlciBmb3IgdGhlIG51bWJlciBvZiBiaW5zIC0tLS0NCiAgICAgIHNsaWRlcklucHV0KGlucHV0SWQgPSAiYmlucyIsDQogICAgICAgICAgICAgICAgICBsYWJlbCA9ICJOdW1iZXIgb2YgYmluczoiLA0KICAgICAgICAgICAgICAgICAgbWluID0gMSwNCiAgICAgICAgICAgICAgICAgIG1heCA9IDUwLA0KICAgICAgICAgICAgICAgICAgdmFsdWUgPSAzMCksDQogICAgICANCiAgICAgIHNlbGVjdElucHV0KGlucHV0SWQgPSAic2VsZWN0IiwgDQogICAgICAgICAgICAgICAgICBsYWJlbCA9ICJQZW5ndWluIFZhcmlhYmxlczoiLCANCiAgICAgICAgICAgICAgICAgIGNob2ljZXMgPSBjKCJCaWxsIExlbmd0aCAobW0pIiA9ICJiaWxsX2xlbmd0aF9tbSIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkJpbGwgRGVwdGggKG1tKSIgPSAiYmlsbF9kZXB0aF9tbSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRmxpcHBlciBMZW5ndGggKG1tKSIgPSAiZmxpcHBlcl9sZW5ndGhfbW0iKSkNCiAgICAgICksDQpgYGANCg0KIyMgRm9ybWF0dGVkIHRleHQNCg0KQW4gZXhhbXBsZSBzb2x1dGlvbiBpcyBwcm92aWRlZCBiZWxvdzoNCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBGLCBlY2hvID0gVH0NCiAgICBzaWRlYmFyUGFuZWwoDQogICAgICANCiAgICAgICAgICAgICMgSW5wdXQ6IFNsaWRlciBmb3IgdGhlIG51bWJlciBvZiBiaW5zIC0tLS0NCiAgICAgICAgICAgIA0KICAgICAgaGVscFRleHQoIlRoaXMgaXMgYSBzbGlkZXIgZm9yIHRoZSBudW1iZXIgb2YgYmlucy4gDQogICAgICAgICAgICAgICBUaGlzIHRleHQgd2lsbCBhcHBlYXIgYWJvdmUgdGhlIHNsaWRlci4iKSwNCiAgICAgIA0KICAgICAgc2xpZGVySW5wdXQoaW5wdXRJZCA9ICJiaW5zIiwNCiAgICAgICAgICAgICAgICAgIGxhYmVsID0gIk51bWJlciBvZiBiaW5zOiIsDQogICAgICAgICAgICAgICAgICBtaW4gPSAxLA0KICAgICAgICAgICAgICAgICAgbWF4ID0gNTAsDQogICAgICAgICAgICAgICAgICB2YWx1ZSA9IDMwKSwNCiAgICAgIA0KICAgICAgc2VsZWN0SW5wdXQoaW5wdXRJZCA9ICJzZWxlY3QiLCANCiAgICAgICAgICAgICAgICAgIGxhYmVsID0gIlBlbmd1aW4gVmFyaWFibGVzOiIsIA0KICAgICAgICAgICAgICAgICAgY2hvaWNlcyA9IGMoIkJpbGwgTGVuZ3RoIChtbSkiID0gImJpbGxfbGVuZ3RoX21tIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQmlsbCBEZXB0aCAobW0pIiA9ICJiaWxsX2RlcHRoX21tIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJGbGlwcGVyIExlbmd0aCAobW0pIiA9ICJmbGlwcGVyX2xlbmd0aF9tbSIpKSwNCiAgICAgIA0KICAgICAgaGVscFRleHQoIlRoaXMgaXMgYSBzZWxlY3Rpb24gYm94LCANCiAgICAgICAgICAgICAgIHdoZXJlIHlvdSBjYW4gc2VsZWN0IHRoZSBQZW5ndWluIHZhcmlhYmxlIHlvdSB3b3VsZCBsaWtlIHRvIHBsb3QuIA0KICAgICAgICAgICAgICAgVGhpcyB0ZXh0IHdpbGwgYXBwZWFyIGJlbG93IHRoZSBzZWxlY3Rpb24gYm94LiIpDQogICAgICANCiAgICAgICksDQpgYGANCg0KIyMNCg0KTm8gYW5zd2VyIHJlcXVpcmVkLg0KDQojIw0KDQpBcyBhbiBleGFtcGxlLCBpZiB5b3VyIFNoaW55IGFwcCBkaXJlY3RvcnkgaXMgDQpgIkM6L1VzZXJzL0plcnJ5L0Rlc2t0b3AvU1RNMTAwMS9wZW5ndWluc19zaGlueV9hcHAiYCB0aGVuIHlvdSB3b3VsZCBjcmVhdGUgeW91ciBgd3d3YCBmb2xkZXIgd2l0aGluIHRoaXMgZGlyZWN0b3J5LCBzbyB5b3VyIGltYWdlIGlzIHNhdmVkIHRvIA0KYCJDOi9Vc2Vycy9KZXJyeS9EZXNrdG9wL1NUTTEwMDEvcGVuZ3VpbnNfc2hpbnlfYXBwL3d3dyJgLg0KDQojIw0KDQpBcyBhbiBleGFtcGxlLCB5b3UgY291bGQgaGF2ZSBpbmNsdWRlZCB0aGUgbGluZSBvZiBjb2RlIA0KDQpgaW1nKHNyYyA9ICJwZW5ndWluLmpwZyIsIGhlaWdodCA9IDIwMCwgd2lkdGggPSAzMDApLGANCg0KYmV0d2VlbiB0aGUgY29kZSBmb3IgdGhlIHR3byB3aWRnZXRzIHNob3duIGluIFxAcmVmKHNpZGViYXIpLg0KDQojIw0KDQpObyBhbnN3ZXIgcmVxdWlyZWQuDQoNCiMjIENoYWxsZW5nZQ0KDQpUaGUgY29tcGxldGUgYGFwcC5SYCBzY3JpcHQgZm9yIHRoZSBDaGFsbGVuZ2UgU2hpbnkgYXBwIGlzIGluY2x1ZGVkIGJlbG93Lg0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIiwgZXZhbCA9IEYsIGVjaG8gPSBUfQ0KbGlicmFyeShzaGlueSkNCmxpYnJhcnkocGFsbWVycGVuZ3VpbnMpDQpwZW5ndWlucyA8LSBuYS5vbWl0KHBlbmd1aW5zKQ0KDQojIERlZmluZSBVSSBmb3IgYXBwIHRoYXQgZHJhd3MgYSBoaXN0b2dyYW0gLS0tLQ0KdWkgPC0gZmx1aWRQYWdlKA0KICANCiAgIyBBcHAgdGl0bGUgLS0tLQ0KICB0aXRsZVBhbmVsKCJQYWxtZXIgUGVuZ3VpbnMgRGF0YSIpLA0KICANCiAgIyBTaWRlYmFyIGxheW91dCB3aXRoIGlucHV0IGFuZCBvdXRwdXQgZGVmaW5pdGlvbnMgLS0tLQ0KICBzaWRlYmFyTGF5b3V0KHBvc2l0aW9uID0gInJpZ2h0IiwNCiAgICANCiAgICAjIFNpZGViYXIgcGFuZWwgZm9yIGlucHV0cyAtLS0tDQogICAgc2lkZWJhclBhbmVsKA0KICAgICAgDQogICAgICBoZWxwVGV4dCgiVGhpcyBpcyBhIHNsaWRlciIpLA0KICAgICAgDQogICAgICAjIElucHV0OiBTbGlkZXIgZm9yIHRoZSBudW1iZXIgb2YgYmlucyAtLS0tDQogICAgICBzbGlkZXJJbnB1dChpbnB1dElkID0gImJpbnMiLA0KICAgICAgICAgICAgICAgICAgbGFiZWwgPSAiTnVtYmVyIG9mIGJpbnM6IiwNCiAgICAgICAgICAgICAgICAgIG1pbiA9IDEsDQogICAgICAgICAgICAgICAgICBtYXggPSA1MCwNCiAgICAgICAgICAgICAgICAgIHZhbHVlID0gMzApLA0KICAgICAgDQogICAgICBpbWcoc3JjID0gImNoaW5zdHJhcC5qcGciLCBoZWlnaHQgPSAyMDAsIHdpZHRoID0gMzAwKSwNCiAgICAgIA0KICAgICAgaGVscFRleHQoIlNlbGVjdCB5LWF4aXMgc2NhdHRlciBwbG90IHZhcmlhYmxlIiksDQogICAgICANCiAgICAgIHNlbGVjdElucHV0KGlucHV0SWQgPSAic2VsZWN0IiwgDQogICAgICAgICAgICAgICAgICBsYWJlbCA9ICJQZW5ndWluIFZhcmlhYmxlczoiLCANCiAgICAgICAgICAgICAgICAgIGNob2ljZXMgPSBjKCJCaWxsIExlbmd0aCAobW0pIiA9ICJiaWxsX2xlbmd0aF9tbSIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkJpbGwgRGVwdGggKG1tKSIgPSAiYmlsbF9kZXB0aF9tbSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRmxpcHBlciBMZW5ndGggKG1tKSIgPSAiZmxpcHBlcl9sZW5ndGhfbW0iKSkNCiAgICAgICksDQogICAgDQogICAgIyBNYWluIHBhbmVsIGZvciBkaXNwbGF5aW5nIG91dHB1dHMgLS0tLQ0KICAgIG1haW5QYW5lbCgNCiAgICAgIA0KICAgICAgcCgiVGhpcyBkYXRhIGlzIGZvciBBZGVsaWUsIENoaW5zdHJhcCBhbmQgR2VudG9vIHBlbmd1aW5zIGZyb20gdGhlIFBhbG1lciBBcmNoaXBlbGFnby4iKSwNCiAgICAgICMgT3V0cHV0OiBIaXN0b2dyYW0gLS0tLQ0KICAgICAgcGxvdE91dHB1dChvdXRwdXRJZCA9ICJkaXN0UGxvdCIpLA0KICAgICAgDQogICAgICBwKCJUaGlzIGhpc3RvZ3JhbSBzaG93cyB0aGUgZGlzdHJpYnV0aW9uIG9mIHBlbmd1aW5zJyBmbGlwcGVyIGxlbmd0aHMgKGluIG1tKS4iKSwNCiAgICAgIA0KICAgICAgcGxvdE91dHB1dChvdXRwdXRJZCA9ICJzY2F0dGVyUGxvdCIpLA0KICAgICAgDQogICAgIHAoIlRoaXMgc2NhdHRlciBwbG90IG1vZGVscyB0aGUgcGVuZ3VpbnMnIGJvZHkgbWFzc2VzIChpbiBncmFtcykgYWdhaW5zdCBvbmUgb2YgYSBzZWxlY3Rpb24gb2Ygb3RoZXIgdmFyaWFibGVzLiIpLA0KICAgICAgDQogICAgKQ0KICApDQopDQoNCiMgRGVmaW5lIHNlcnZlciBsb2dpYyByZXF1aXJlZCB0byBkcmF3IGEgaGlzdG9ncmFtIC0tLS0NCnNlcnZlciA8LSBmdW5jdGlvbihpbnB1dCwgb3V0cHV0KSB7DQogIA0KICBvdXRwdXQkZGlzdFBsb3QgPC0gcmVuZGVyUGxvdCh7DQogICAgDQogICAgeCAgICA8LSBwZW5ndWlucyRmbGlwcGVyX2xlbmd0aF9tbQ0KICAgIGJpbnMgPC0gc2VxKG1pbih4KSwgbWF4KHgpLCBsZW5ndGgub3V0ID0gaW5wdXQkYmlucyArIDEpDQogICAgDQogICAgaGlzdCh4LCBicmVha3MgPSBiaW5zLCBjb2wgPSAiIzc1QUFEQiIsIGJvcmRlciA9ICJ3aGl0ZSIsDQogICAgICAgICB4bGFiID0gIkZsaXBwZXIgbGVuZ3RoIG9mIHBlbmd1aW5zIChtbSkiLA0KICAgICAgICAgbWFpbiA9ICJIaXN0b2dyYW0gb2YgUGVuZ3VpbiBGbGlwcGVyIExlbmd0aHMiKQ0KICB9KQ0KICANCm91dHB1dCRzY2F0dGVyUGxvdCA8LSByZW5kZXJQbG90KHsNCg0KICB3IDwtIHBlbmd1aW5zWywgYygiYm9keV9tYXNzX2ciLCBpbnB1dCRzZWxlY3QpLCBkcm9wID0gRkFMU0VdDQogIA0KICBwbG90KHcsIGNvbCA9ICIjNzVBQURCIikNCiAgDQp9KQ0KICANCn0NCg0Kc2hpbnlBcHAodWkgPSB1aSwgc2VydmVyID0gc2VydmVyKQ0KYGBgDQoNCiMgUHVibGlzaGluZyB5b3VyIFNoaW55IEFwcA0KDQpQbGVhc2UgcmVmZXIgdG8gW1NlY3Rpb24gNiBvZiB0aGUgRGF0YSBWaXN1YWxpc2F0aW9uIGluIFIgc3VwcGxlbWVudF0oaHR0cHM6Ly9ib29rZG93bi5vcmcvcmVoay9zdG0xMDAxX2RzbV90MV9kYXRhX3Zpc3VhbGlzYXRpb25faW5fci9jcmVhdGluZy13ZWItYXBwcy11c2luZy1zaGlueS1jb21wdXRlci1sYWItNGIuaHRtbCkuDQoNCjxicj4NCg0KIyMjIyBUaGF0J3MgZXZlcnl0aGluZyBjb3ZlcmVkLCB3ZWxsIGRvbmUuICMjIyMgey19DQoNCjxicj4NCg0KIyBSZWZlcmVuY2VzIHstICNSZWZ9DQo8ZGl2IGlkPSJyZWZzIj48L2Rpdj4NCg0KPGJyPg0KDQo8Zm9udCBjb2xvciA9ICJncmV5Ij4NClRoZXNlIG5vdGVzIGhhdmUgYmVlbiBwcmVwYXJlZCBieSBSdXBlcnQgS3V2ZWtlLiBQbGVhc2Ugbm90ZSB0aGF0IHNvbWUgb2YgdGhlIGNvbnRlbnQgaW4gdGhlc2Ugbm90ZXMgaGFzIGJlZW4gZGV2ZWxvcGVkIGZyb20gWyp0aGUgUiBTdHVkaW8gU2hpbnkgVHV0b3JpYWwqXShodHRwczovL3NoaW55LnJzdHVkaW8uY29tL3R1dG9yaWFsLykgW0Byc3R1ZGlvXS4gVGhlIGNvcHlyaWdodCBmb3IgdGhlIG1hdGVyaWFsIGluIHRoZXNlIG5vdGVzIHJlc2lkZXMgd2l0aCB0aGUgYXV0aG9ycyBuYW1lZCBhYm92ZSwgd2l0aCB0aGUgRGVwYXJ0bWVudCBvZiBNYXRoZW1hdGljYWwgYW5kIFBoeXNpY2FsIFNjaWVuY2VzIGFuZCB3aXRoIExhIFRyb2JlIFVuaXZlcnNpdHkuIENvcHlyaWdodCBpbiB0aGlzIHdvcmsgaXMgdmVzdGVkIGluIExhIFRyb2JlIFVuaXZlcnNpdHkgaW5jbHVkaW5nIGFsbCBMYSBUcm9iZSBVbml2ZXJzaXR5IGJyYW5kaW5nIGFuZCBuYW1pbmcuIFVubGVzcyBvdGhlcndpc2Ugc3RhdGVkLCBtYXRlcmlhbCB3aXRoaW4gdGhpcyB3b3JrIGlzIGxpY2Vuc2VkIHVuZGVyIGEgQ3JlYXRpdmUgQ29tbW9ucyBBdHRyaWJ1dGlvbi1Ob24gQ29tbWVyY2lhbC1Ob24gRGVyaXZhdGl2ZXMgTGljZW5zZSANCjxhIGhyZWYgPSAiaHR0cHM6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL2xpY2Vuc2VzL2J5LW5jLW5kLzQuMC9DQyIgdGFyZ2V0PSJfYmxhbmsiPiBCWS1OQy1ORC4gPC9hPg0KPC9mb250Pg==