Setup

To find other tutorials for this class, go to the main website, https://ds112-lendway.netlify.app/.

Welcome to another tutorial for this class, COMP/STAT 112: Introduction to Data Science! It will be similar to the others, including demo videos and files embedded in this document and practice problems with hints or solutions at the end. There are some new libraries, so be sure to install those first. There are also some additional instructions (especially if you’re using the server) down below the demo section.

As most of our files do, we start this one with three R code chunks: 1. options, 2. libraries and settings, 3. data.

knitr::opts_chunk$set(echo = TRUE, 
                      message = FALSE, 
                      warning = FALSE)
library(tidyverse)     # for data cleaning and plotting
library(gardenR)       # for Lisa's garden data
library(lubridate)     # for date manipulation
library(openintro)     # for the abbr2state() function
library(palmerpenguins)# for Palmer penguin data
library(maps)          # for map data
library(ggmap)         # for mapping points on maps
library(gplots)        # for col2hex() function
library(RColorBrewer)  # for color palettes
library(sf)            # for working with spatial data
library(leaflet)       # for highly customizable mapping
library(ggthemes)      # for more themes (including theme_map())
library(plotly)        # for the ggplotly() - basic interactivity
library(gganimate)     # for adding animation layers to ggplots
library(gifski)        # for creating the gif (don't need to load this library every time,but need it installed)
library(transformr)    # for "tweening" (gganimate)
library(shiny)         # for creating interactive apps
theme_set(theme_minimal())
# Lisa's garden data
data("garden_harvest")

Learning Goals

After this tutorial, you should be able to do the following:

  • Add basic interactivity to a ggplot2 plot using ggplotly().

  • Add animation layers to plots using gganimate functions.

  • Create a shiny app that requires inputs.

  • Publish a shiny app to shinyapps.io.

Easy interactivity with plotly

Probably the easiest way to add interactivity to a plot created with ggplot2 is by using the ggplotly() function from the plotly library. The plotly package can do A LOT more than what we’ll cover in this course as it is a plotting framework if its own. But, it can do a lot with just that one function.

Let’s look at an example. In the code below, I compute the cumulative harvest in pounds by vegetable and create a bar graph. I save the graph and print it out. The code and graph should be familiar.

veggie_harvest_graph <- garden_harvest %>% 
  group_by(vegetable) %>% 
  summarize(total_wt_lbs = sum(weight)*0.00220462) %>% 
  ggplot() +
  geom_col(aes(x = total_wt_lbs, 
               y = fct_reorder(vegetable, 
                               total_wt_lbs, 
                               .desc = FALSE))) +
  labs(title = "Total Harvest by vegetable (lb)", 
       x = "",
       y = "")

veggie_harvest_graph

Now, we plotly-ify it!

ggplotly(veggie_harvest_graph)

The labeling is fairly ugly in the graph above. I can fix some of that by editing my original plot. In the code below, I add a text aesthetic, which will be used in ggplotly() to display the vegetable name, and use tooltip to tell it the aesthetics to display when scrolling over the graph.

veggie_harvest_graph2 <- garden_harvest %>% 
  group_by(vegetable) %>%
  summarize(total_wt_lbs = sum(weight)*0.00220462) %>%
  ggplot() +
  geom_col(aes(x = total_wt_lbs,
               y = fct_reorder(vegetable,
                               total_wt_lbs,
                               .desc = FALSE),
               text = vegetable)) +
  labs(title = "Total Harvest by vegetable (lb)",
       x = "",
       y = "")

ggplotly(veggie_harvest_graph2,
         tooltip = c("text", "x"))

This works for many different types of plots created with ggplot2.

Your turn!

Exercise

In this exercise, choose 2 graphs you have created for ANY assignment in this class and add interactivity using the ggplotly() function.

Adding animation with gganimate

Key functions

The gganimate package works well with ggplot2 functions by providing additional grammar that assists in adding animation to the plots. These functions get added as layers in ggplot(), just like you are used to adding geom_*() layers and other layers that modify the graph.

From Thomas Pedersen’s documentation, here are the key functions/grammar of the package:

  • transition_*() defines how the data should be spread out and how it relates to itself across time (time is not always actual time).
  • view_*() defines how the positional scales should change along the animation.
  • shadow_*() defines how data from other points in time should be presented in the given point in time.
  • enter_*()/exit_*() defines how new data should appear and how old data should disappear during the course of the animation.
  • ease_aes() defines how different aesthetics should be eased during transitions.

You only need a transition_*() or view_*() function to add animation. This tutorial focuses on three transition_*() functions: transition_states(), transition_time(), and transition_reveal().

Creating an animated plot with gganimate

  1. Create your base ggplot()
  2. Add appropriate geom_*() layers.
  3. Add any other stylistic additions (themes, titles, etc.)
  4. Add gganimate transition_*() layer
  5. Add gganimate options, which may include making some changes in the ggplot() code.

transition_*() functions

The following image, taken from the gganimate cheatsheet, gives a nice overview of the three functions.

transition_states()

This transition is used to transition between distinct stages of the data. We will show an example of transitioning between levels of a categorical variable. We will use the garden_harvest dataset and will follow the steps outlined above for creating an animated plot.

First, we create a dataset of daily tomato harvests in pounds for each variety of tomato. We add day of week and reorder variety from most to least harvested.

daily_tomato <- garden_harvest %>% 
  filter(vegetable == "tomatoes") %>% 
  group_by(variety, date) %>% 
  summarize(daily_harvest = sum(weight)*0.00220462) %>% 
  mutate(day_of_week = wday(date, label = TRUE)) %>% 
  ungroup() %>% 
  mutate(variety = fct_reorder(variety, daily_harvest, sum, .desc = TRUE)) 

daily_tomato

Next, we create a jittered scatterplot of daily harvest by day of week. We facet the plot by variety.

daily_tomato %>% 
  ggplot(aes(x = daily_harvest, 
             y = day_of_week)) +
  geom_jitter() +
  facet_wrap(vars(variety)) +
  labs(title = "Daily tomato harvest",
       x = "",
       y = "") 

Now, instead of looking at the data by faceting, we will use animation and transition by variety. This code takes a while to run. And the animation shows up over in the Viewer in the lower right-hand pane, rather than in the preview below the code chunk.

daily_tomato %>% 
  ggplot(aes(x = daily_harvest,
             y = day_of_week)) +
  geom_jitter() +
  labs(title = "Daily tomato harvest",
       x = "",
       y = "") +
  transition_states(variety)

Because it takes a while to create the animation, you don’t want to recreate it each time you knit your file. So, in the code chunk where you create the animation, add eval=FALSE to the code chunk options (ie. inside the curly brackets next to the lowercase r).

Then, save the gif using the anim_save() function, like in the code below. The name in quotes is the name of the file that will be created, which needs to end in .gif. This will automatically save your most recent gganimate plot. So, be sure to run the code right after you create the animation. Alternatively, you can save your gganimate, say you called it plot1 and do anim_save(plot_1, "tomatoes1.gif"). This will be saved to your working directory. If you are working in a project (hopefully the one linked to your GitHub repo, right?), then this will go to the main folder for the project if that is where the .Rmd file is located.

anim_save("tomatoes1.gif")

Then, load the file back in using the following code. You can add echo=FALSE to the code chunk options to omit displaying the code.

knitr::include_graphics("tomatoes1.gif")

Now, let’s return to the animation that was created. There are a couple things we should fix. One is that as it animates, it looks like the observations from one variety morph into the observations from the next variety. We can fix this in two ways. One, is to color by variety:

daily_tomato %>% 
  ggplot(aes(x = daily_harvest,
             y = day_of_week,
             color = variety)) +
  geom_jitter() +
  scale_color_viridis_d(option = "magma") +
  labs(title = "Daily tomato harvest",
       x = "",
       y = "",
       color = "") +
  theme(legend.position = "none") +
  transition_states(variety)

Another, is to map variety to the group aesthetic (This is the recommended way to do it, even if we also color by variety.):

daily_tomato %>% 
  ggplot(aes(x = daily_harvest,
             y = day_of_week,
             group = variety)) +
  geom_jitter() +
  labs(title = "Daily tomato harvest",
       x = "",
       y = "") +
  transition_states(variety)

Another issue is that we don’t see the variety names as it animates through. Thankfully, the various transition_*() functions create some useful variables we can use to display the names of variety. The variables created are shown below.

From transition_states() help

We can access the variables by putting them in square brackets inside a label. Below, I use the closest_state variable that is created to add the variety to the subtitle of the plot.

daily_tomato %>% 
  ggplot(aes(x = daily_harvest,
             y = day_of_week,
             group = variety)) +
  geom_jitter() +
  labs(title = "Daily tomato harvest",
       subtitle = "Variety: {closest_state}",
       x = "",
       y = "") +
  transition_states(variety)

There are many options we can change. Below, we make a couple more changes.

  • Save the animated plot as tomato_gganim and output the animation using animate() in order to control the duration (there are other options in that function, too).

  • Change the relative transition lengths (how long it takes to switch variety) and state lengths (how long it stays on a variety). These are relative lengths, so the transition time is twice as long as the time spent in a state.

  • Shrink the points as variety transitions using exit_shrink().

  • Color the points light blue as they enter and exit.

tomato_gganim <- daily_tomato %>% 
  ggplot(aes(x = daily_harvest,
             y = day_of_week,
             group = variety)) +
  geom_jitter() +
  labs(title = "Daily tomato harvest",
       subtitle = "Variety: {closest_state}",
       x = "",
       y = "") +
  transition_states(variety,
                    transition_length = 2,
                    state_length = 1) +
  exit_shrink() +
  enter_recolor(color = "lightblue") +
  exit_recolor(color = "lightblue")

animate(tomato_gganim, duration = 20)

transition_time()

This transition is used to transition between distinct states in time. We will show an example of transitioning over harvest dates in the garden_harvest dataset. We will follow the steps outlined earlier for creating an animated plot.

First, we create a dataset of daily harvest in pounds for a subset of four vegetables.

daily_harvest_subset <- garden_harvest %>% 
  filter(vegetable %in% c("tomatoes", "beans", 
                          "peas", "zucchini")) %>% 
  group_by(vegetable, date) %>% 
  summarize(daily_harvest_lb = sum(weight)*0.00220462) 

daily_harvest_subset

Then, we create a static plot, coloring the points differently and assigning different shapes to distinguish the various green colors.

daily_harvest_subset %>% 
  ggplot(aes(x = date, 
             y = daily_harvest_lb,
             color = vegetable,
             shape = vegetable)) +
  geom_point() +
  labs(title = "Daily harvest (lb)", 
       x = "",
       y = "",
       color = "vegetable",
       shape = "vegetable") +
  scale_color_manual(values = c("tomatoes" = "darkred",
                       "beans" = "springgreen4",
                       "peas" = "yellowgreen",
                       "zucchini" = "darkgreen")) +
  theme(legend.position = "top",
        legend.title = element_blank()) 

Now we animate the plot, transiting over time by date.

daily_harvest_subset %>% 
  ggplot(aes(x = date,
             y = daily_harvest_lb,
             color = vegetable,
             shape = vegetable)) +
  geom_point() +
  labs(title = "Daily harvest (lb)",
       x = "",
       y = "",
       color = "vegetable",
       shape = "vegetable") +
  scale_color_manual(values = c("tomatoes" = "darkred",
                                "beans" = "springgreen4",
                                "peas" = "yellowgreen",
                                "zucchini" = "darkgreen")) +
  theme(legend.position = "top",
        legend.title = element_blank()) +
  transition_time(date)

Now, let’s try adding some other features:

  • Keep a little history of the data via shadow_wake()

  • Fade the old data points out via exit_fade()

  • Add a date subtitle using the frame_time variable created from transition_time().

daily_harvest_subset %>% 
  ggplot(aes(x = date,
             y = daily_harvest_lb,
             color = vegetable,
             shape = vegetable)) +
  geom_point() +
  labs(title = "Daily harvest (lb)",
       subtitle = "Date: {frame_time}",
       x = "",
       y = "",
       color = "vegetable",
       shape = "vegetable") +
  scale_color_manual(values = c("tomatoes" = "darkred",
                                "beans" = "springgreen4",
                                "peas" = "yellowgreen",
                                "zucchini" = "darkgreen")) +
  theme(legend.position = "top",
        legend.title = element_blank()) +
  transition_time(date) +
  shadow_wake(wake_length = .3) +
  exit_fade()

transition_reveal()

This transition allows you to let data gradually appear. We will show an example of building up the cumulative harvest data over harvest dates using the garden_harvest dataset. We will follow the steps outlined earlier for creating an animated plot.

First we create a dataset of cumulative harvest by date for a subset of vegetables.

cum_harvest_subset <- garden_harvest %>% 
  filter(vegetable %in% c("tomatoes", "beans", 
                          "peas", "zucchini")) %>% 
  group_by(vegetable, date) %>% 
  summarize(daily_harvest_lb = sum(weight)*0.00220462) %>% 
  mutate(cum_harvest_lb = cumsum(daily_harvest_lb))

cum_harvest_subset

Next, we create a static plot of cumulative harvest, coloring the lines by vegetable.

cum_harvest_subset %>% 
  ggplot(aes(x = date, 
             y = cum_harvest_lb,
             color = vegetable)) +
  geom_line() +
  labs(title = "Cumulative harvest (lb)", 
       x = "",
       y = "",
       color = "vegetable") +
  scale_color_manual(values = c("tomatoes" = "darkred",
                       "beans" = "springgreen4",
                       "peas" = "yellowgreen",
                       "zucchini" = "darkgreen")) +
  theme(legend.position = "top",
        legend.title = element_blank())

And now, add animation!

cum_harvest_subset %>% 
  ggplot(aes(x = date,
             y = cum_harvest_lb,
             color = vegetable)) +
  geom_line() +
  labs(title = "Cumulative harvest (lb)",
       x = "",
       y = "",
       color = "vegetable") +
  scale_color_manual(values = c("tomatoes" = "darkred",
                       "beans" = "springgreen4",
                       "peas" = "yellowgreen",
                       "zucchini" = "darkgreen")) +
  theme(legend.position = "top",
        legend.title = element_blank()) +
  transition_reveal(date)

And now let’s do a couple things to improve the plot:

  • Remove the legend and add text that shows vegetable name on the plot (I love this!).

  • Add date to the subtitle.

cum_harvest_subset %>% 
  ggplot(aes(x = date,
             y = cum_harvest_lb,
             color = vegetable)) +
  geom_line() +
  geom_text(aes(label = vegetable)) +
  labs(title = "Cumulative harvest (lb)",
       subtitle = "Date: {frame_along}",
       x = "",
       y = "",
       color = "vegetable") +
  scale_color_manual(values = c("tomatoes" = "darkred",
                       "beans" = "springgreen4",
                       "peas" = "yellowgreen",
                       "zucchini" = "darkgreen")) +
  theme(legend.position = "none") +
  transition_reveal(date)

We could have used this same data with a different type of transition. It’s always good to think about the point you are trying to make with the animation.

cum_harvest_subset %>% 
  ggplot(aes(x = date,
             y = cum_harvest_lb,
             color = vegetable)) +
  geom_line() +
  labs(title = "Cumulative harvest (lb)",
       subtitle = "Vegetable: {closest_state}",
       x = "",
       y = "",
       color = "vegetable") +
  scale_color_manual(values = c("tomatoes" = "darkred",
                       "beans" = "springgreen4",
                       "peas" = "yellowgreen",
                       "zucchini" = "darkgreen")) +
  theme(legend.position = "none") +
  transition_states(vegetable)

Resources

Setting up gganimate

This package might require some extra setup.

  • Make sure you can load the following packages: gganimate, gifski, transformr. First, try just installing gganimate and see if you can load all the other packages after only installing that one. If so, you are done. If not, try installing the other packages. After you install them all, RESTART RStudio. Hopefully you have success at that point. If not, talk to me. If you are using Macalester’s server, you will likely have to do the next step.

  • If you use Macalester’s server, you will almost surely get an error when you try to install gifski. The error will say something about not have RUST and will direct you to the Rust website. Click Getting started.

    • In the terminal, try typing rustup update. If that is successful, you are done. If it tells you something about not having Rust, then go to the next step.
    • If the previous step was not successful, try running the following in the terminal: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh (this is from the Click Getting started Rust page and could be slightly out of date - go to that page to assure the code is correct). This may run on its own or it may give you some options. Always type the yes options in the terminal.
    • After installing, close the session (go to house icon in top right) and open a new session.
    • Now install the gifski library. If that runs successfully, close the R session and open a new one. If that was all successful, you should be able to create animations!

Your turn!

Use animation to tell an interesting story with the small_trains dataset that contains data from the SNCF (National Society of French Railways). These are Tidy Tuesday data! Read more about it here.

small_trains <- read_csv("https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2019/2019-02-26/small_trains.csv") 

Creating an app with shiny

In this section, we will learn how to create a Shiny App. Shiny Apps are applications that allow the user to interact or make changes to graphs or tables. You will learn how to create them in R and how to deploy them on your own shinyapps.io webpage. See examples of some apps here.

Concept Map

The concept map below illustrates the key components of a shiny app and how they relate to one another. We will go through more detail during the tutorial.

Slides

I am doing this tutorial a bit differently than I’ve done other tutorials. I am going to walk you through the creation of a shiny app by following the intro_to_shiny slides on my GitHub page. You can download the slides below. I will include short screen captures to illustrate how to do each part on your own.

Getting the files

To begin, you are going to copy everything from my GitHub repo to your own GitHub repo. You do this by forking. From your GitHub account, search for mine: llendway/intro_to_shiny. Once there, click the fork button. Then, all my files will be in a repo of the same name on your GitHub page.

From there, clone the repo and create a new project in R Studio.

Once you’ve done that, you can access all the files from your computer. As you make changes, you can commit and push them out to your own GitHub account, if you’d like.

Creating an app

Creating a Shiny app is different from what we’ve done so far in this class. One of the biggest changes, is that we’ll be working from .R files, rather than .Rmd files. In .R files, everything is read as R code. So, it’s like one big R code chunk. If you want to make comments, you need to use the pound/hashtag symbol, #.

Let’s start by opening the basic_app_template.R file in the app_files folder. Since you’ve cloned the repo, this will be one of the files in your project folder. Make sure you have the project open first! Open the file and click Run App. This is a really boring app - there is nothing there! But, it is a great starting point because it gives you an outline of what you need in order to make your app work.

Before getting into a lot of details, let’s add a little bit to the app. At the top of the file, load the tidyverse and babynames libraries and add some text between quotes inside the fluidPage() function.Run the app. You can check that you did this right by looking in the basic_app_add_more.R file.

Now, let’s move on to creating a more interesting app. The goal is to create a Shiny app for my kids to explore the babynames dataset! Remember, that’s their favorite.

Requirements:

  • Show the number of babies with a given name over time.
  • Allow the user to type a single name. (Ideally, it should give a message if there are no records of the name that was typed, but we won’t worry about that now.)
  • Allow the user to choose a range of years they would like to display.
  • Allow the user to filter by sex.

The details

How do we do this?

Setup:

  1. Create folder (either create a new project or put it in the project folder you forked and cloned from my repo). Give the folder a name that describes the app.
  2. Open new R Script file (not .Rmd file)
  3. Copy and paste code from the basic_app_add_more.R file
  4. Save the R Script file as app.R into the folder you just created.

Fun parts:

  1. Add ui components
  2. Add server components
  3. Deploy to shinyapps.io to share with the world!

Let’s learn more about these fun parts!

What is an app.R file?

Like we’ve already seen in the sample app files, these contain two key components:

  • ui: the user interface. This is the webpage that your user will interact with. Don’t worry, you don’t need to know how to write html! The app will do that for you! (Although if you want to, there are opportunities to use html.)

  • server: the computer part. What should the computer/server do with your inputs as the user changes them. This section will have R code in it, more like we’re used to … sort of.

I always keep these names as the default. The last chunk of code at the bottom, shinyApp(ui = ui, server = server), will compile everything together to result in the interactive webpage.

We will add different *Input() and *Output() functions to the ui.

  • The *Input*() functions collect inputs from the user.
  • The *Output() functions work with the render*() functions in the server portion to to add R output to the UI.

Have the cheatsheet open at all times! It is extremely helpful.

*Input() functions

The *Input*() functions collect inputs from the user. The various types are listed on the right-hand side of the first page of the cheatsheet. You will list all the *Input() functions you want to use with their accompanying arguments inside the fluidPage() function in the ui portion. Separate the *Input() functions with commas. In the basic_app_template, add three inputs inside the fluidPage() function. Be sure to separate them with commas.

In all the *Input() functions, the first two arguments are the same: inputId is how you will call this input in the server portion later, label is how this will actually be labeled in your UI. Each function has some additional arguments depending what you want to do.

In the app.R file you just created, add three inputs inside the fluidPage() function. Be sure to separate them with commas.

  • sliderInput() to choose the start and end year for the eventual graph.
  • textInput() to write a name.
  • selectInput() to choose a sex.

Once you complete all the necessary arguments, run your app. Make sure you can enter and move things around as expected. There won’t be a graph yet because we haven’t created it. You can check your results by looking at the babynames_app_step1.R file (I’ve also added a couple extra things - see if you can figure out what they do).

*Output() functions

*Output() functions in the ui portion work with the render*() functions in the server portion to to add R output to the UI. The *Output() functions are listed in the bottom center part of the first page of the cheatsheet.

All the *Output() functions have the same first argument, outputId, which is used how you will call this output in the server portion later (like the inputId in the *Input() functions).

Now, add a plotOutput() to the fluidPage() function. Run the app with the output. You can check your work by looking at the babynames_app_step2.R file. Notice that nothing really changes. Think of this output as a placeholder. So, it knows there is going to be a plot in the UI, but the details of what the plot will look like and the R code to create it will be in the server portion. Let’s talk about that!

Using render*() functions

In the server portion of the code, we will use render*() functions with R code to communicate how to use the input pieces along with the R code to create the desired output. The render*() function you use will depend on the desired output. The bottom center of the cheatsheet shows how *Output() and render*() functions connect.

In general, the server section of code will look something like this:

server <- function(input, output) {
  output$outputId_of_interest <- render*({
R code that creates the output and calls various input$InputId's
  })
}

So, if inside the ui part, we did plotOutput(outputId = "timeplot"), then in the server part, we would use output$timeplot <- renderPlot({...}) and put in detailed R code in place of the .... To reference the inputs we created in the ui, we use input$inputID_name. So, for example, if we had an *Input() with inputId = "years", we would use input$years in the server portion.

Now, since we are interested in creating a plot, add the renderPlot() function inside the server portion of the code. Reference the inputs you’ve already created in previous parts and use filter() and ggplot() to render the desired interactive plot. Run the app and check your work by looking at the babynames_app_basic.R file or in the code chunk below (so don’t look ahead, if you want to try it out on your own!). I also added a submit button to the UI so everything is updated at the same time, only when the button is clicked. You should try adding that, too.

Below, I have also included all the R code used to create the app. I have highlighted the inputId and outputId similarly in the ui and server portions to draw attention to where they are referenced.

library(shiny)
library(tidyverse)
library(babynames)

ui <- fluidPage(
  sliderInput(inputId = "years",
              label = "Year Range",
              min = 1880,
              max = 2019,
              value = c(1880,2019),
              sep = ""),
  textInput("name",
            "Name",
            value = "",
            placeholder = "Lisa"),
  selectInput("sex",
              "Sex",
              choices = list(Female = "F", Male = "M")),
  submitButton(text = "Create my plot!"),
  plotOutput(outputId = "timeplot")
)

server <- function(input, output) {
  output$timeplot <- renderPlot({
  babynames %>%
    filter(name == input$name,
           sex == input$sex) %>%
    ggplot() +
    geom_line(aes(x = year, y = n)) +
    scale_x_continuous(limits = input$years) +
    theme_minimal()
  })
}

shinyApp(ui = ui, server = server)

Publishing your app

  1. Install and load the rsconnect library in your R Studio session.
  2. Register at https://www.shinyapps.io/admin/#/signup.
  3. Once you are logged in to shinyapps.io, go to Account –> Tokens and click the Show button.
  4. Copy and paste the code into the console in R. This will connect your account to R Studio.
  5. When you create an app, save it as app.R in a folder. It MUST be named app.R. In the app.R file, load all libraries you use in your code. Also try not to have extra libraries or it will take longer to get it on the website. Any data that your app uses needs to be read in within the app. If the data are local to your computer, you need to have the data in the same folder as the app.
  6. Run the app. In the upper right-hand corner, there is an option to publish the app. Click on that. It will take a bit of time to do it the first time. Once published, you can go to the app via the webpage provided.

The instructions are set out in more detail here.

You can see the example of my sample app before it was published in the shiny_app folder of all my files (note that the rsconnect folder is added after publishing the app). The published app is here.

Demo video

You can watch a video of me creating this same shiny app to better see how all the pieces fit together. This video is a bit long, but you can always skip through some parts.

Voicethread: Creating a shiny app demo

Resources

Part I of Garrett Grolemund’s tutorial - through the shinyapps.io section.

Shiny cheatsheet

Lisa’s Intro to Shiny GitHub page

Your turn!

Try adding something more to the app. Here are a couple ideas.

  1. Return a message if the user enters a name incorrectly. By the way, this will happen if someone enters a name with all lowercase letters, too. How could you solve that problem?

  2. Allow the user to enter more than one name and compare the graphs either by coloring different lines or using faceting.

  3. Allow the user to change something about the graph, like the color of the line or the type of line.

LS0tCnRpdGxlOiAiQ3JlYXRpbmcgaW50ZXJhY3RpdmUgYW5kIGFuaW1hdGVkIHBsb3RzIGluIFIiCm91dHB1dDogCiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBkZl9wcmludDogcGFnZWQKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKLS0tCgojIyBTZXR1cAoKVG8gZmluZCBvdGhlciB0dXRvcmlhbHMgZm9yIHRoaXMgY2xhc3MsIGdvIHRvIHRoZSBtYWluIHdlYnNpdGUsIFtodHRwczovL2RzMTEyLWxlbmR3YXkubmV0bGlmeS5hcHAvXShodHRwczovL2RzMTEyLWxlbmR3YXkubmV0bGlmeS5hcHAvKS4KCldlbGNvbWUgdG8gYW5vdGhlciB0dXRvcmlhbCBmb3IgdGhpcyBjbGFzcywgQ09NUC9TVEFUIDExMjogKkludHJvZHVjdGlvbiB0byBEYXRhIFNjaWVuY2UqISBJdCB3aWxsIGJlIHNpbWlsYXIgdG8gdGhlIG90aGVycywgaW5jbHVkaW5nIGRlbW8gdmlkZW9zIGFuZCBmaWxlcyBlbWJlZGRlZCBpbiB0aGlzIGRvY3VtZW50IGFuZCBwcmFjdGljZSBwcm9ibGVtcyB3aXRoIGhpbnRzIG9yIHNvbHV0aW9ucyBhdCB0aGUgZW5kLiBUaGVyZSBhcmUgc29tZSBuZXcgbGlicmFyaWVzLCBzbyBiZSBzdXJlIHRvIGluc3RhbGwgdGhvc2UgZmlyc3QuIFRoZXJlIGFyZSBhbHNvIHNvbWUgYWRkaXRpb25hbCBpbnN0cnVjdGlvbnMgKGVzcGVjaWFsbHkgaWYgeW91J3JlIHVzaW5nIHRoZSBzZXJ2ZXIpIGRvd24gYmVsb3cgdGhlIGRlbW8gc2VjdGlvbi4KCkFzIG1vc3Qgb2Ygb3VyIGZpbGVzIGRvLCB3ZSBzdGFydCB0aGlzIG9uZSB3aXRoIHRocmVlIFIgY29kZSBjaHVua3M6IDEuIG9wdGlvbnMsIDIuIGxpYnJhcmllcyBhbmQgc2V0dGluZ3MsIDMuIGRhdGEuIAoKYGBge3Igc2V0dXB9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgCiAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlID0gRkFMU0UsIAogICAgICAgICAgICAgICAgICAgICAgd2FybmluZyA9IEZBTFNFKQpgYGAKCmBgYHtyIGxpYnJhcmllc30KbGlicmFyeSh0aWR5dmVyc2UpICAgICAjIGZvciBkYXRhIGNsZWFuaW5nIGFuZCBwbG90dGluZwpsaWJyYXJ5KGdhcmRlblIpICAgICAgICMgZm9yIExpc2EncyBnYXJkZW4gZGF0YQpsaWJyYXJ5KGx1YnJpZGF0ZSkgICAgICMgZm9yIGRhdGUgbWFuaXB1bGF0aW9uCmxpYnJhcnkob3BlbmludHJvKSAgICAgIyBmb3IgdGhlIGFiYnIyc3RhdGUoKSBmdW5jdGlvbgpsaWJyYXJ5KHBhbG1lcnBlbmd1aW5zKSMgZm9yIFBhbG1lciBwZW5ndWluIGRhdGEKbGlicmFyeShtYXBzKSAgICAgICAgICAjIGZvciBtYXAgZGF0YQpsaWJyYXJ5KGdnbWFwKSAgICAgICAgICMgZm9yIG1hcHBpbmcgcG9pbnRzIG9uIG1hcHMKbGlicmFyeShncGxvdHMpICAgICAgICAjIGZvciBjb2wyaGV4KCkgZnVuY3Rpb24KbGlicmFyeShSQ29sb3JCcmV3ZXIpICAjIGZvciBjb2xvciBwYWxldHRlcwpsaWJyYXJ5KHNmKSAgICAgICAgICAgICMgZm9yIHdvcmtpbmcgd2l0aCBzcGF0aWFsIGRhdGEKbGlicmFyeShsZWFmbGV0KSAgICAgICAjIGZvciBoaWdobHkgY3VzdG9taXphYmxlIG1hcHBpbmcKbGlicmFyeShnZ3RoZW1lcykgICAgICAjIGZvciBtb3JlIHRoZW1lcyAoaW5jbHVkaW5nIHRoZW1lX21hcCgpKQpsaWJyYXJ5KHBsb3RseSkgICAgICAgICMgZm9yIHRoZSBnZ3Bsb3RseSgpIC0gYmFzaWMgaW50ZXJhY3Rpdml0eQpsaWJyYXJ5KGdnYW5pbWF0ZSkgICAgICMgZm9yIGFkZGluZyBhbmltYXRpb24gbGF5ZXJzIHRvIGdncGxvdHMKbGlicmFyeShnaWZza2kpICAgICAgICAjIGZvciBjcmVhdGluZyB0aGUgZ2lmIChkb24ndCBuZWVkIHRvIGxvYWQgdGhpcyBsaWJyYXJ5IGV2ZXJ5IHRpbWUsYnV0IG5lZWQgaXQgaW5zdGFsbGVkKQpsaWJyYXJ5KHRyYW5zZm9ybXIpICAgICMgZm9yICJ0d2VlbmluZyIgKGdnYW5pbWF0ZSkKbGlicmFyeShzaGlueSkgICAgICAgICAjIGZvciBjcmVhdGluZyBpbnRlcmFjdGl2ZSBhcHBzCnRoZW1lX3NldCh0aGVtZV9taW5pbWFsKCkpCmBgYAoKYGBge3IgbXlfbGlicmFyaWVzLCBpbmNsdWRlPUZBTFNFfQojIExpc2EgbmVlZHMgdGhpcywgc3R1ZGVudHMgZG9uJ3QKbGlicmFyeShkb3dubG9hZHRoaXMpICMgZm9yIGluY2x1ZGluZyBkb3dubG9hZCBidXR0b25zIGZvciBmaWxlcwpsaWJyYXJ5KGZsYWlyKSAgICAgICAgIyBmb3IgaGlnaGxpZ2h0aW5nIGNvZGUKbGlicmFyeSh4YXJpbmdhbkV4dHJhKSMgZm9yIHNtYWxsIHNsaWRlcyBhbmQgb3RoZXIgY29vbCB0aGluZ3MKYGBgCgpgYGB7ciBkYXRhfQojIExpc2EncyBnYXJkZW4gZGF0YQpkYXRhKCJnYXJkZW5faGFydmVzdCIpCmBgYAoKIyMgTGVhcm5pbmcgR29hbHMKCkFmdGVyIHRoaXMgdHV0b3JpYWwsIHlvdSBzaG91bGQgYmUgYWJsZSB0byBkbyB0aGUgZm9sbG93aW5nOgoKKiBBZGQgYmFzaWMgaW50ZXJhY3Rpdml0eSB0byBhIGBnZ3Bsb3QyYCBwbG90IHVzaW5nIGBnZ3Bsb3RseSgpYC4gIAoKKiBBZGQgYW5pbWF0aW9uIGxheWVycyB0byBwbG90cyB1c2luZyBgZ2dhbmltYXRlYCBmdW5jdGlvbnMuICAKCiogQ3JlYXRlIGEgc2hpbnkgYXBwIHRoYXQgcmVxdWlyZXMgaW5wdXRzLiAgCgoqIFB1Ymxpc2ggYSBzaGlueSBhcHAgdG8gc2hpbnlhcHBzLmlvLgoKCiMjIEVhc3kgaW50ZXJhY3Rpdml0eSB3aXRoIGBwbG90bHlgCgpQcm9iYWJseSB0aGUgZWFzaWVzdCB3YXkgdG8gYWRkIGludGVyYWN0aXZpdHkgdG8gYSBwbG90IGNyZWF0ZWQgd2l0aCBgZ2dwbG90MmAgaXMgYnkgdXNpbmcgdGhlIGBnZ3Bsb3RseSgpYCBmdW5jdGlvbiBmcm9tIHRoZSBgcGxvdGx5YCBsaWJyYXJ5LiBUaGUgYHBsb3RseWAgcGFja2FnZSBjYW4gZG8gQSBMT1QgbW9yZSB0aGFuIHdoYXQgd2UnbGwgY292ZXIgaW4gdGhpcyBjb3Vyc2UgYXMgaXQgaXMgYSBwbG90dGluZyBmcmFtZXdvcmsgaWYgaXRzIG93bi4gQnV0LCBpdCBjYW4gZG8gYSBsb3Qgd2l0aCBqdXN0IHRoYXQgb25lIGZ1bmN0aW9uLiAKCkxldCdzIGxvb2sgYXQgYW4gZXhhbXBsZS4gSW4gdGhlIGNvZGUgYmVsb3csIEkgY29tcHV0ZSB0aGUgY3VtdWxhdGl2ZSBoYXJ2ZXN0IGluIHBvdW5kcyBieSB2ZWdldGFibGUgYW5kIGNyZWF0ZSBhIGJhciBncmFwaC4gIEkgc2F2ZSB0aGUgZ3JhcGggYW5kIHByaW50IGl0IG91dC4gVGhlIGNvZGUgYW5kIGdyYXBoIHNob3VsZCBiZSBmYW1pbGlhci4KCmBgYHtyfQp2ZWdnaWVfaGFydmVzdF9ncmFwaCA8LSBnYXJkZW5faGFydmVzdCAlPiUgCiAgZ3JvdXBfYnkodmVnZXRhYmxlKSAlPiUgCiAgc3VtbWFyaXplKHRvdGFsX3d0X2xicyA9IHN1bSh3ZWlnaHQpKjAuMDAyMjA0NjIpICU+JSAKICBnZ3Bsb3QoKSArCiAgZ2VvbV9jb2woYWVzKHggPSB0b3RhbF93dF9sYnMsIAogICAgICAgICAgICAgICB5ID0gZmN0X3Jlb3JkZXIodmVnZXRhYmxlLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvdGFsX3d0X2xicywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAuZGVzYyA9IEZBTFNFKSkpICsKICBsYWJzKHRpdGxlID0gIlRvdGFsIEhhcnZlc3QgYnkgdmVnZXRhYmxlIChsYikiLCAKICAgICAgIHggPSAiIiwKICAgICAgIHkgPSAiIikKCnZlZ2dpZV9oYXJ2ZXN0X2dyYXBoCmBgYAoKTm93LCB3ZSBgcGxvdGx5YC1pZnkgaXQhCgpgYGB7cn0KZ2dwbG90bHkodmVnZ2llX2hhcnZlc3RfZ3JhcGgpCmBgYAoKVGhlIGxhYmVsaW5nIGlzIGZhaXJseSB1Z2x5IGluIHRoZSBncmFwaCBhYm92ZS4gSSBjYW4gZml4IHNvbWUgb2YgdGhhdCBieSBlZGl0aW5nIG15IG9yaWdpbmFsIHBsb3QuIEluIHRoZSBjb2RlIGJlbG93LCBJIGFkZCBhIGB0ZXh0YCBhZXN0aGV0aWMsIHdoaWNoIHdpbGwgYmUgdXNlZCBpbiBgZ2dwbG90bHkoKWAgdG8gZGlzcGxheSB0aGUgdmVnZXRhYmxlIG5hbWUsIGFuZCB1c2UgYHRvb2x0aXBgIHRvIHRlbGwgaXQgdGhlIGFlc3RoZXRpY3MgdG8gZGlzcGxheSB3aGVuIHNjcm9sbGluZyBvdmVyIHRoZSBncmFwaC4KCmBgYHtyIGdncGxvdGx5LWV4LCBlY2hvPUZBTFNFLCBldmFsPUZBTFNFfQp2ZWdnaWVfaGFydmVzdF9ncmFwaDIgPC0gZ2FyZGVuX2hhcnZlc3QgJT4lIAogIGdyb3VwX2J5KHZlZ2V0YWJsZSkgJT4lIAogIHN1bW1hcml6ZSh0b3RhbF93dF9sYnMgPSBzdW0od2VpZ2h0KSowLjAwMjIwNDYyKSAlPiUgCiAgZ2dwbG90KCkgKwogIGdlb21fY29sKGFlcyh4ID0gdG90YWxfd3RfbGJzLCAKICAgICAgICAgICAgICAgeSA9IGZjdF9yZW9yZGVyKHZlZ2V0YWJsZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0b3RhbF93dF9sYnMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLmRlc2MgPSBGQUxTRSksCiAgICAgICAgICAgICAgIHRleHQgPSB2ZWdldGFibGUpKSArCiAgbGFicyh0aXRsZSA9ICJUb3RhbCBIYXJ2ZXN0IGJ5IHZlZ2V0YWJsZSAobGIpIiwgCiAgICAgICB4ID0gIiIsCiAgICAgICB5ID0gIiIpCgpnZ3Bsb3RseSh2ZWdnaWVfaGFydmVzdF9ncmFwaDIsCiAgICAgICAgIHRvb2x0aXAgPSBjKCJ0ZXh0IiwgIngiKSkKYGBgCgoKYGBge3IsIGVjaG89RkFMU0V9CmRlY29yYXRlX2NodW5rKCJnZ3Bsb3RseS1leCIpICU+JSAKICBmbGFpcigidGV4dCA9IHZlZ2V0YWJsZSIpICU+JSAKICBmbGFpcigidG9vbHRpcCA9IikKYGBgCgoKVGhpcyB3b3JrcyBmb3IgbWFueSBkaWZmZXJlbnQgdHlwZXMgb2YgcGxvdHMgY3JlYXRlZCB3aXRoIGBnZ3Bsb3QyYC4gCgojIyMgUmVzb3VyY2VzCgpbUGxvdGx5J3MgZ2dwbG90MiBpbnRlZ3JhdGlvbl0oaHR0cHM6Ly9wbG90bHkuY29tL2dncGxvdDIvKQoKIyMjIFlvdXIgdHVybiEKCiMjIyMgRXhlcmNpc2UKCkluIHRoaXMgZXhlcmNpc2UsIGNob29zZSAyIGdyYXBocyB5b3UgaGF2ZSBjcmVhdGVkIGZvciBBTlkgYXNzaWdubWVudCBpbiB0aGlzIGNsYXNzIGFuZCBhZGQgaW50ZXJhY3Rpdml0eSB1c2luZyB0aGUgYGdncGxvdGx5KClgIGZ1bmN0aW9uLiAKCiMjIEFkZGluZyBhbmltYXRpb24gd2l0aCBgZ2dhbmltYXRlYAoKIyMjIEtleSBmdW5jdGlvbnMKClRoZSBgZ2dhbmltYXRlYCBwYWNrYWdlIHdvcmtzIHdlbGwgd2l0aCBgZ2dwbG90MmAgZnVuY3Rpb25zIGJ5IHByb3ZpZGluZyBhZGRpdGlvbmFsIGdyYW1tYXIgdGhhdCBhc3Npc3RzIGluIGFkZGluZyBhbmltYXRpb24gdG8gdGhlIHBsb3RzLiBUaGVzZSBmdW5jdGlvbnMgZ2V0IGFkZGVkIGFzIGxheWVycyBpbiBgZ2dwbG90KClgLCBqdXN0IGxpa2UgeW91IGFyZSB1c2VkIHRvIGFkZGluZyBgZ2VvbV8qKClgIGxheWVycyBhbmQgb3RoZXIgbGF5ZXJzIHRoYXQgbW9kaWZ5IHRoZSBncmFwaC4KCkZyb20gVGhvbWFzIFBlZGVyc2VuJ3MgZG9jdW1lbnRhdGlvbiwgaGVyZSBhcmUgdGhlIGtleSBmdW5jdGlvbnMvZ3JhbW1hciBvZiB0aGUgcGFja2FnZToKCiogYHRyYW5zaXRpb25fKigpYCBkZWZpbmVzIGhvdyB0aGUgZGF0YSBzaG91bGQgYmUgc3ByZWFkIG91dCBhbmQgaG93IGl0IHJlbGF0ZXMgdG8gaXRzZWxmIGFjcm9zcyB0aW1lICh0aW1lIGlzIG5vdCBhbHdheXMgYWN0dWFsIHRpbWUpLgoqIGB2aWV3XyooKWAgZGVmaW5lcyBob3cgdGhlIHBvc2l0aW9uYWwgc2NhbGVzIHNob3VsZCBjaGFuZ2UgYWxvbmcgdGhlIGFuaW1hdGlvbi4KKiBgc2hhZG93XyooKWAgZGVmaW5lcyBob3cgZGF0YSBmcm9tIG90aGVyIHBvaW50cyBpbiB0aW1lIHNob3VsZCBiZSBwcmVzZW50ZWQgaW4gdGhlIGdpdmVuIHBvaW50IGluIHRpbWUuCiogYGVudGVyXyooKS9leGl0XyooKWAgZGVmaW5lcyBob3cgbmV3IGRhdGEgc2hvdWxkIGFwcGVhciBhbmQgaG93IG9sZCBkYXRhIHNob3VsZCBkaXNhcHBlYXIgZHVyaW5nIHRoZSBjb3Vyc2Ugb2YgdGhlIGFuaW1hdGlvbi4KKiBgZWFzZV9hZXMoKWAgZGVmaW5lcyBob3cgZGlmZmVyZW50IGFlc3RoZXRpY3Mgc2hvdWxkIGJlIGVhc2VkIGR1cmluZyB0cmFuc2l0aW9ucy4KCllvdSBvbmx5IG5lZWQgYSBgdHJhbnNpdGlvbl8qKClgIG9yIGB2aWV3XyooKWAgZnVuY3Rpb24gdG8gYWRkIGFuaW1hdGlvbi4gVGhpcyB0dXRvcmlhbCBmb2N1c2VzIG9uIHRocmVlIGB0cmFuc2l0aW9uXyooKWAgZnVuY3Rpb25zOiBgdHJhbnNpdGlvbl9zdGF0ZXMoKWAsIGB0cmFuc2l0aW9uX3RpbWUoKWAsIGFuZCBgdHJhbnNpdGlvbl9yZXZlYWwoKWAuIAoKIyMjIENyZWF0aW5nIGFuIGFuaW1hdGVkIHBsb3Qgd2l0aCBgZ2dhbmltYXRlYAoKMS4gQ3JlYXRlIHlvdXIgYmFzZSBgZ2dwbG90KClgICAKMi4gQWRkIGFwcHJvcHJpYXRlIGBnZW9tXyooKWAgbGF5ZXJzLiAgCjMuIEFkZCBhbnkgb3RoZXIgc3R5bGlzdGljIGFkZGl0aW9ucyAodGhlbWVzLCB0aXRsZXMsIGV0Yy4pICAKNC4gQWRkIGBnZ2FuaW1hdGVgIGB0cmFuc2l0aW9uXyooKWAgbGF5ZXIgIAo1LiBBZGQgYGdnYW5pbWF0ZWAgb3B0aW9ucywgd2hpY2ggbWF5IGluY2x1ZGUgbWFraW5nIHNvbWUgY2hhbmdlcyBpbiB0aGUgYGdncGxvdCgpYCBjb2RlLgoKIyMjIGB0cmFuc2l0aW9uXyooKWAgZnVuY3Rpb25zCgpUaGUgZm9sbG93aW5nIGltYWdlLCB0YWtlbiBmcm9tIHRoZSBnZ2FuaW1hdGUgY2hlYXRzaGVldCwgZ2l2ZXMgYSBuaWNlIG92ZXJ2aWV3IG9mIHRoZSB0aHJlZSBmdW5jdGlvbnMuIAoKIVtJbWFnZSBmcm9tOiBodHRwczovL3Vnb3Byb3RvLmdpdGh1Yi5pby91Z29fcl9kb2MvcGRmL2dnYW5pbWF0ZS5wZGZdKC4uLy4uL2ltYWdlcy9nZ2FuaW1hdGVfdHJhbnNpdGlvbnMucG5nKQoKIyMjIyBgdHJhbnNpdGlvbl9zdGF0ZXMoKWAKClRoaXMgdHJhbnNpdGlvbiBpcyB1c2VkIHRvIHRyYW5zaXRpb24gYmV0d2VlbiBkaXN0aW5jdCBzdGFnZXMgb2YgdGhlIGRhdGEuIFdlIHdpbGwgc2hvdyBhbiBleGFtcGxlIG9mIHRyYW5zaXRpb25pbmcgYmV0d2VlbiBsZXZlbHMgb2YgYSBjYXRlZ29yaWNhbCB2YXJpYWJsZS4gV2Ugd2lsbCB1c2UgdGhlIGBnYXJkZW5faGFydmVzdGAgZGF0YXNldCBhbmQgd2lsbCBmb2xsb3cgdGhlIHN0ZXBzIG91dGxpbmVkIGFib3ZlIGZvciBjcmVhdGluZyBhbiBhbmltYXRlZCBwbG90LgoKRmlyc3QsIHdlIGNyZWF0ZSBhIGRhdGFzZXQgb2YgZGFpbHkgdG9tYXRvIGhhcnZlc3RzIGluIHBvdW5kcyBmb3IgZWFjaCB2YXJpZXR5IG9mIHRvbWF0by4gV2UgYWRkIGRheSBvZiB3ZWVrIGFuZCByZW9yZGVyIHZhcmlldHkgZnJvbSBtb3N0IHRvIGxlYXN0IGhhcnZlc3RlZC4KCmBgYHtyfQpkYWlseV90b21hdG8gPC0gZ2FyZGVuX2hhcnZlc3QgJT4lIAogIGZpbHRlcih2ZWdldGFibGUgPT0gInRvbWF0b2VzIikgJT4lIAogIGdyb3VwX2J5KHZhcmlldHksIGRhdGUpICU+JSAKICBzdW1tYXJpemUoZGFpbHlfaGFydmVzdCA9IHN1bSh3ZWlnaHQpKjAuMDAyMjA0NjIpICU+JSAKICBtdXRhdGUoZGF5X29mX3dlZWsgPSB3ZGF5KGRhdGUsIGxhYmVsID0gVFJVRSkpICU+JSAKICB1bmdyb3VwKCkgJT4lIAogIG11dGF0ZSh2YXJpZXR5ID0gZmN0X3Jlb3JkZXIodmFyaWV0eSwgZGFpbHlfaGFydmVzdCwgc3VtLCAuZGVzYyA9IFRSVUUpKSAKCmRhaWx5X3RvbWF0bwpgYGAgCgpOZXh0LCB3ZSBjcmVhdGUgYSBqaXR0ZXJlZCBzY2F0dGVycGxvdCBvZiBkYWlseSBoYXJ2ZXN0IGJ5IGRheSBvZiB3ZWVrLiBXZSBmYWNldCB0aGUgcGxvdCBieSB2YXJpZXR5LgoKYGBge3J9CmRhaWx5X3RvbWF0byAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gZGFpbHlfaGFydmVzdCwgCiAgICAgICAgICAgICB5ID0gZGF5X29mX3dlZWspKSArCiAgZ2VvbV9qaXR0ZXIoKSArCiAgZmFjZXRfd3JhcCh2YXJzKHZhcmlldHkpKSArCiAgbGFicyh0aXRsZSA9ICJEYWlseSB0b21hdG8gaGFydmVzdCIsCiAgICAgICB4ID0gIiIsCiAgICAgICB5ID0gIiIpIApgYGAKCk5vdywgaW5zdGVhZCBvZiBsb29raW5nIGF0IHRoZSBkYXRhIGJ5IGZhY2V0aW5nLCB3ZSB3aWxsIHVzZSBhbmltYXRpb24gYW5kIHRyYW5zaXRpb24gYnkgdmFyaWV0eS4gVGhpcyBjb2RlIHRha2VzIGEgd2hpbGUgdG8gcnVuLiBBbmQgdGhlIGFuaW1hdGlvbiBzaG93cyB1cCBvdmVyIGluIHRoZSBWaWV3ZXIgaW4gdGhlIGxvd2VyIHJpZ2h0LWhhbmQgcGFuZSwgcmF0aGVyIHRoYW4gaW4gdGhlIHByZXZpZXcgYmVsb3cgdGhlIGNvZGUgY2h1bmsuCgpgYGB7ciB0b21hdG8tZ2dhbmltYXRlLCBldmFsPUZBTFNFLCBlY2hvPUZBTFNFfQpkYWlseV90b21hdG8gJT4lIAogIGdncGxvdChhZXMoeCA9IGRhaWx5X2hhcnZlc3QsIAogICAgICAgICAgICAgeSA9IGRheV9vZl93ZWVrKSkgKwogIGdlb21faml0dGVyKCkgKwogIGxhYnModGl0bGUgPSAiRGFpbHkgdG9tYXRvIGhhcnZlc3QiLAogICAgICAgeCA9ICIiLAogICAgICAgeSA9ICIiKSArCiAgdHJhbnNpdGlvbl9zdGF0ZXModmFyaWV0eSkKYGBgCgoKYGBge3IsIGVjaG89RkFMU0V9CmRlY29yYXRlX2NodW5rKCJ0b21hdG8tZ2dhbmltYXRlIiwgZXZhbD1GQUxTRSkgJT4lIAogIGZsYWlyKCJ0cmFuc2l0aW9uX3N0YXRlcyh2YXJpZXR5KSIpCmBgYAoKYGBge3IsIGV2YWw9RkFMU0UsIGVjaG89RkFMU0V9CmFuaW1fc2F2ZSgidG9tYXRvZXMxLmdpZiIpCmBgYAoKYGBge3IsIGVjaG89RkFMU0V9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJ0b21hdG9lczEuZ2lmIikKYGBgCgpCZWNhdXNlIGl0IHRha2VzIGEgd2hpbGUgdG8gY3JlYXRlIHRoZSBhbmltYXRpb24sIHlvdSBkb24ndCB3YW50IHRvIHJlY3JlYXRlIGl0IGVhY2ggdGltZSB5b3Uga25pdCB5b3VyIGZpbGUuIFNvLCBpbiB0aGUgY29kZSBjaHVuayB3aGVyZSB5b3UgY3JlYXRlIHRoZSBhbmltYXRpb24sIGFkZCBgZXZhbD1GQUxTRWAgdG8gdGhlIGNvZGUgY2h1bmsgb3B0aW9ucyAoaWUuIGluc2lkZSB0aGUgY3VybHkgYnJhY2tldHMgbmV4dCB0byB0aGUgbG93ZXJjYXNlIHIpLiAKClRoZW4sIHNhdmUgdGhlIGdpZiB1c2luZyB0aGUgYGFuaW1fc2F2ZSgpYCBmdW5jdGlvbiwgbGlrZSBpbiB0aGUgY29kZSBiZWxvdy4gVGhlIG5hbWUgaW4gcXVvdGVzIGlzIHRoZSBuYW1lIG9mIHRoZSBmaWxlIHRoYXQgd2lsbCBiZSBjcmVhdGVkLCB3aGljaCBuZWVkcyB0byBlbmQgaW4gLmdpZi4gVGhpcyB3aWxsIGF1dG9tYXRpY2FsbHkgc2F2ZSB5b3VyICptb3N0IHJlY2VudCogYGdnYW5pbWF0ZWAgcGxvdC4gU28sIGJlIHN1cmUgdG8gcnVuIHRoZSBjb2RlIHJpZ2h0IGFmdGVyIHlvdSBjcmVhdGUgdGhlIGFuaW1hdGlvbi4gQWx0ZXJuYXRpdmVseSwgeW91IGNhbiBzYXZlIHlvdXIgYGdnYW5pbWF0ZWAsIHNheSB5b3UgY2FsbGVkIGl0IGBwbG90MWAgYW5kIGRvIGBhbmltX3NhdmUocGxvdF8xLCAidG9tYXRvZXMxLmdpZiIpYC4gVGhpcyB3aWxsIGJlIHNhdmVkIHRvIHlvdXIgKndvcmtpbmcgZGlyZWN0b3J5Ki4gSWYgeW91IGFyZSB3b3JraW5nIGluIGEgcHJvamVjdCAoaG9wZWZ1bGx5IHRoZSBvbmUgbGlua2VkIHRvIHlvdXIgR2l0SHViIHJlcG8sIHJpZ2h0PyksIHRoZW4gdGhpcyB3aWxsIGdvIHRvIHRoZSBtYWluIGZvbGRlciBmb3IgdGhlIHByb2plY3QgaWYgdGhhdCBpcyB3aGVyZSB0aGUgLlJtZCBmaWxlIGlzIGxvY2F0ZWQuIAoKYGBge3IsIGV2YWw9RkFMU0V9CmFuaW1fc2F2ZSgidG9tYXRvZXMxLmdpZiIpCmBgYAoKVGhlbiwgbG9hZCB0aGUgZmlsZSBiYWNrIGluIHVzaW5nIHRoZSBmb2xsb3dpbmcgY29kZS4gWW91IGNhbiBhZGQgYGVjaG89RkFMU0VgIHRvIHRoZSBjb2RlIGNodW5rIG9wdGlvbnMgdG8gb21pdCBkaXNwbGF5aW5nIHRoZSBjb2RlLiAKCmBgYHtyLCBldmFsPUZBTFNFfQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygidG9tYXRvZXMxLmdpZiIpCmBgYAoKTm93LCBsZXQncyByZXR1cm4gdG8gdGhlIGFuaW1hdGlvbiB0aGF0IHdhcyBjcmVhdGVkLiBUaGVyZSBhcmUgYSBjb3VwbGUgdGhpbmdzIHdlIHNob3VsZCBmaXguIE9uZSBpcyB0aGF0IGFzIGl0IGFuaW1hdGVzLCBpdCBsb29rcyBsaWtlIHRoZSBvYnNlcnZhdGlvbnMgZnJvbSBvbmUgYHZhcmlldHlgIG1vcnBoIGludG8gdGhlIG9ic2VydmF0aW9ucyBmcm9tIHRoZSBuZXh0IGB2YXJpZXR5LmAgV2UgY2FuIGZpeCB0aGlzIGluIHR3byB3YXlzLiBPbmUsIGlzIHRvIGNvbG9yIGJ5IGB2YXJpZXR5YDogCgpgYGB7ciB0b21hdG8tZ2dhbmltYXRlMiwgZXZhbD1GQUxTRSwgZWNobz1GQUxTRX0KZGFpbHlfdG9tYXRvICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBkYWlseV9oYXJ2ZXN0LCAKICAgICAgICAgICAgIHkgPSBkYXlfb2Zfd2VlaywKICAgICAgICAgICAgIGNvbG9yID0gdmFyaWV0eSkpICsKICBnZW9tX2ppdHRlcigpICsKICBzY2FsZV9jb2xvcl92aXJpZGlzX2Qob3B0aW9uID0gIm1hZ21hIikgKwogIGxhYnModGl0bGUgPSAiRGFpbHkgdG9tYXRvIGhhcnZlc3QiLAogICAgICAgeCA9ICIiLAogICAgICAgeSA9ICIiLAogICAgICAgY29sb3IgPSAiIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogIHRyYW5zaXRpb25fc3RhdGVzKHZhcmlldHkpCmBgYAoKYGBge3IsIGVjaG89RkFMU0V9CmRlY29yYXRlX2NodW5rKCJ0b21hdG8tZ2dhbmltYXRlMiIsIGV2YWw9RkFMU0UpICU+JSAKICBmbGFpcigiY29sb3IgPSB2YXJpZXR5IikgJT4lIAogIGZsYWlyKCdzY2FsZV9jb2xvcl92aXJpZGlzX2Qob3B0aW9uID0gIm1hZ21hIiknKSAlPiUgCiAgZmxhaXIoImNvbG9yID0iKSAlPiUgCiAgZmxhaXIoJ3RoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiknKQpgYGAKCmBgYHtyLCBldmFsPUZBTFNFLCBlY2hvPUZBTFNFfQphbmltX3NhdmUoInRvbWF0b2VzMi5naWYiKQpgYGAKCmBgYHtyLCBlY2hvPUZBTFNFfQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygidG9tYXRvZXMyLmdpZiIpCmBgYAoKQW5vdGhlciwgaXMgdG8gbWFwIGB2YXJpZXR5YCB0byB0aGUgYGdyb3VwYCBhZXN0aGV0aWMgKFRoaXMgaXMgdGhlIHJlY29tbWVuZGVkIHdheSB0byBkbyBpdCwgZXZlbiBpZiB3ZSBhbHNvIGNvbG9yIGJ5IGB2YXJpZXR5YC4pOiAKCmBgYHtyIHRvbWF0by1nZ2FuaW1hdGUzLCBldmFsPUZBTFNFLCBlY2hvPUZBTFNFfQpkYWlseV90b21hdG8gJT4lIAogIGdncGxvdChhZXMoeCA9IGRhaWx5X2hhcnZlc3QsIAogICAgICAgICAgICAgeSA9IGRheV9vZl93ZWVrLAogICAgICAgICAgICAgZ3JvdXAgPSB2YXJpZXR5KSkgKwogIGdlb21faml0dGVyKCkgKwogIGxhYnModGl0bGUgPSAiRGFpbHkgdG9tYXRvIGhhcnZlc3QiLAogICAgICAgeCA9ICIiLAogICAgICAgeSA9ICIiKSArCiAgdHJhbnNpdGlvbl9zdGF0ZXModmFyaWV0eSkKYGBgCgpgYGB7ciwgZWNobz1GQUxTRX0KZGVjb3JhdGVfY2h1bmsoInRvbWF0by1nZ2FuaW1hdGUzIiwgZXZhbD1GQUxTRSkgJT4lIAogIGZsYWlyKCJncm91cCA9IHZhcmlldHkiKQpgYGAKCmBgYHtyLCBldmFsPUZBTFNFLCBlY2hvPUZBTFNFfQphbmltX3NhdmUoInRvbWF0b2VzMy5naWYiKQpgYGAKCmBgYHtyLCBlY2hvPUZBTFNFfQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygidG9tYXRvZXMzLmdpZiIpCmBgYAoKQW5vdGhlciBpc3N1ZSBpcyB0aGF0IHdlIGRvbid0IHNlZSB0aGUgYHZhcmlldHlgIG5hbWVzIGFzIGl0IGFuaW1hdGVzIHRocm91Z2guIFRoYW5rZnVsbHksIHRoZSB2YXJpb3VzIGB0cmFuc2l0aW9uXyooKWAgZnVuY3Rpb25zIGNyZWF0ZSBzb21lIHVzZWZ1bCB2YXJpYWJsZXMgd2UgY2FuIHVzZSB0byBkaXNwbGF5IHRoZSBuYW1lcyBvZiBgdmFyaWV0eWAuIFRoZSB2YXJpYWJsZXMgY3JlYXRlZCBhcmUgc2hvd24gYmVsb3cuIAoKIVtGcm9tIGB0cmFuc2l0aW9uX3N0YXRlcygpYCBoZWxwXSguLi8uLi9pbWFnZXMvZ2dhbmltYXRlX3RyYW5zaXRpb25fc3RhdGVfdmFycy5wbmcpCgpXZSBjYW4gYWNjZXNzIHRoZSB2YXJpYWJsZXMgYnkgcHV0dGluZyB0aGVtIGluIHNxdWFyZSBicmFja2V0cyBpbnNpZGUgYSBsYWJlbC4gQmVsb3csIEkgdXNlIHRoZSBgY2xvc2VzdF9zdGF0ZWAgdmFyaWFibGUgdGhhdCBpcyBjcmVhdGVkIHRvIGFkZCB0aGUgYHZhcmlldHlgIHRvIHRoZSBzdWJ0aXRsZSBvZiB0aGUgcGxvdC4KCmBgYHtyIHRvbWF0by1nZ2FuaW1hdGU0LCBldmFsPUZBTFNFLCBlY2hvPUZBTFNFfQpkYWlseV90b21hdG8gJT4lIAogIGdncGxvdChhZXMoeCA9IGRhaWx5X2hhcnZlc3QsIAogICAgICAgICAgICAgeSA9IGRheV9vZl93ZWVrLAogICAgICAgICAgICAgZ3JvdXAgPSB2YXJpZXR5KSkgKwogIGdlb21faml0dGVyKCkgKwogIGxhYnModGl0bGUgPSAiRGFpbHkgdG9tYXRvIGhhcnZlc3QiLAogICAgICAgc3VidGl0bGUgPSAiVmFyaWV0eToge2Nsb3Nlc3Rfc3RhdGV9IiwKICAgICAgIHggPSAiIiwKICAgICAgIHkgPSAiIikgKwogIHRyYW5zaXRpb25fc3RhdGVzKHZhcmlldHkpCmBgYAoKYGBge3IsIGVjaG89RkFMU0V9CmRlY29yYXRlX2NodW5rKCJ0b21hdG8tZ2dhbmltYXRlNCIsIGV2YWw9RkFMU0UpICU+JSAKICBmbGFpcignc3VidGl0bGUgPSAiVmFyaWV0eToge2Nsb3Nlc3Rfc3RhdGV9IicpCmBgYAoKYGBge3IsIGV2YWw9RkFMU0UsIGVjaG89RkFMU0V9CmFuaW1fc2F2ZSgidG9tYXRvZXM0LmdpZiIpCmBgYAoKYGBge3IsIGVjaG89RkFMU0V9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJ0b21hdG9lczQuZ2lmIikKYGBgCgpUaGVyZSBhcmUgbWFueSBvcHRpb25zIHdlIGNhbiBjaGFuZ2UuIEJlbG93LCB3ZSBtYWtlIGEgY291cGxlIG1vcmUgY2hhbmdlcy4KCiogU2F2ZSB0aGUgYW5pbWF0ZWQgcGxvdCBhcyBgdG9tYXRvX2dnYW5pbWAgYW5kIG91dHB1dCB0aGUgYW5pbWF0aW9uIHVzaW5nIGBhbmltYXRlKClgIGluIG9yZGVyIHRvIGNvbnRyb2wgdGhlIGR1cmF0aW9uICh0aGVyZSBhcmUgb3RoZXIgb3B0aW9ucyBpbiB0aGF0IGZ1bmN0aW9uLCB0b28pLgoKKiBDaGFuZ2UgdGhlIHJlbGF0aXZlIHRyYW5zaXRpb24gbGVuZ3RocyAoaG93IGxvbmcgaXQgdGFrZXMgdG8gc3dpdGNoIGB2YXJpZXR5YCkgYW5kIHN0YXRlIGxlbmd0aHMgKGhvdyBsb25nIGl0IHN0YXlzIG9uIGEgdmFyaWV0eSkuIFRoZXNlIGFyZSByZWxhdGl2ZSBsZW5ndGhzLCBzbyB0aGUgdHJhbnNpdGlvbiB0aW1lIGlzIHR3aWNlIGFzIGxvbmcgYXMgdGhlIHRpbWUgc3BlbnQgaW4gYSBzdGF0ZS4gIAoKKiBTaHJpbmsgdGhlIHBvaW50cyBhcyBgdmFyaWV0eWAgdHJhbnNpdGlvbnMgdXNpbmcgYGV4aXRfc2hyaW5rKClgLiAgCgoqIENvbG9yIHRoZSBwb2ludHMgbGlnaHQgYmx1ZSBhcyB0aGV5IGVudGVyIGFuZCBleGl0LiAKCmBgYHtyIHRvbWF0by1nZ2FuaW1hdGU1LCBldmFsPUZBTFNFLCBlY2hvPUZBTFNFfQp0b21hdG9fZ2dhbmltIDwtIGRhaWx5X3RvbWF0byAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gZGFpbHlfaGFydmVzdCwgCiAgICAgICAgICAgICB5ID0gZGF5X29mX3dlZWssCiAgICAgICAgICAgICBncm91cCA9IHZhcmlldHkpKSArCiAgZ2VvbV9qaXR0ZXIoKSArCiAgbGFicyh0aXRsZSA9ICJEYWlseSB0b21hdG8gaGFydmVzdCIsCiAgICAgICBzdWJ0aXRsZSA9ICJWYXJpZXR5OiB7Y2xvc2VzdF9zdGF0ZX0iLAogICAgICAgeCA9ICIiLAogICAgICAgeSA9ICIiKSArCiAgdHJhbnNpdGlvbl9zdGF0ZXModmFyaWV0eSwgCiAgICAgICAgICAgICAgICAgICAgdHJhbnNpdGlvbl9sZW5ndGggPSAyLCAKICAgICAgICAgICAgICAgICAgICBzdGF0ZV9sZW5ndGggPSAxKSArCiAgZXhpdF9zaHJpbmsoKSArCiAgZW50ZXJfcmVjb2xvcihjb2xvciA9ICJsaWdodGJsdWUiKSArCiAgZXhpdF9yZWNvbG9yKGNvbG9yID0gImxpZ2h0Ymx1ZSIpCgphbmltYXRlKHRvbWF0b19nZ2FuaW0sIGR1cmF0aW9uID0gMjApCmBgYAoKYGBge3IsIGVjaG89RkFMU0V9CmRlY29yYXRlX2NodW5rKCJ0b21hdG8tZ2dhbmltYXRlNSIsIGV2YWw9RkFMU0UpICU+JSAKICBmbGFpcigidHJhbnNpdGlvbl9sZW5ndGggPSAyIikgJT4lIAogIGZsYWlyKCJzdGF0ZV9sZW5ndGggPSAxIikgJT4lIAogIGZsYWlyKCJleGl0X3NocmluaygpIikgJT4lIAogIGZsYWlyKCdlbnRlcl9yZWNvbG9yKGNvbG9yID0gImxpZ2h0Ymx1ZSIpJykgJT4lIAogIGZsYWlyKCdleGl0X3JlY29sb3IoY29sb3IgPSAibGlnaHRibHVlIiknKSAlPiUgCiAgZmxhaXIoImFuaW1hdGUodG9tYXRvX2dnYW5pbSwgZHVyYXRpb24gPSAyMCkiKSAlPiUgCiAgZmxhaXIoInRvbWF0b19nZ2FuaW0gPC0iKQpgYGAKCmBgYHtyLCBldmFsPUZBTFNFLCBlY2hvPUZBTFNFfQphbmltX3NhdmUoInRvbWF0b2VzNS5naWYiKQpgYGAKCmBgYHtyLCBlY2hvPUZBTFNFfQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygidG9tYXRvZXM1LmdpZiIpCmBgYAoKIyMjIyBgdHJhbnNpdGlvbl90aW1lKClgCgpUaGlzIHRyYW5zaXRpb24gaXMgdXNlZCB0byB0cmFuc2l0aW9uIGJldHdlZW4gZGlzdGluY3Qgc3RhdGVzIGluIHRpbWUuIFdlIHdpbGwgc2hvdyBhbiBleGFtcGxlIG9mIHRyYW5zaXRpb25pbmcgb3ZlciBoYXJ2ZXN0IGRhdGVzIGluIHRoZSBgZ2FyZGVuX2hhcnZlc3RgIGRhdGFzZXQuICBXZSB3aWxsIGZvbGxvdyB0aGUgc3RlcHMgb3V0bGluZWQgZWFybGllciBmb3IgY3JlYXRpbmcgYW4gYW5pbWF0ZWQgcGxvdC4KCgpGaXJzdCwgd2UgY3JlYXRlIGEgZGF0YXNldCBvZiBkYWlseSBoYXJ2ZXN0IGluIHBvdW5kcyBmb3IgYSBzdWJzZXQgb2YgZm91ciB2ZWdldGFibGVzLgoKYGBge3J9CmRhaWx5X2hhcnZlc3Rfc3Vic2V0IDwtIGdhcmRlbl9oYXJ2ZXN0ICU+JSAKICBmaWx0ZXIodmVnZXRhYmxlICVpbiUgYygidG9tYXRvZXMiLCAiYmVhbnMiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAicGVhcyIsICJ6dWNjaGluaSIpKSAlPiUgCiAgZ3JvdXBfYnkodmVnZXRhYmxlLCBkYXRlKSAlPiUgCiAgc3VtbWFyaXplKGRhaWx5X2hhcnZlc3RfbGIgPSBzdW0od2VpZ2h0KSowLjAwMjIwNDYyKSAKCmRhaWx5X2hhcnZlc3Rfc3Vic2V0CmBgYAoKVGhlbiwgd2UgY3JlYXRlIGEgc3RhdGljIHBsb3QsIGNvbG9yaW5nIHRoZSBwb2ludHMgZGlmZmVyZW50bHkgYW5kIGFzc2lnbmluZyBkaWZmZXJlbnQgc2hhcGVzIHRvIGRpc3Rpbmd1aXNoIHRoZSB2YXJpb3VzIGdyZWVuIGNvbG9ycy4gCgpgYGB7cn0KZGFpbHlfaGFydmVzdF9zdWJzZXQgJT4lIAogIGdncGxvdChhZXMoeCA9IGRhdGUsIAogICAgICAgICAgICAgeSA9IGRhaWx5X2hhcnZlc3RfbGIsCiAgICAgICAgICAgICBjb2xvciA9IHZlZ2V0YWJsZSwKICAgICAgICAgICAgIHNoYXBlID0gdmVnZXRhYmxlKSkgKwogIGdlb21fcG9pbnQoKSArCiAgbGFicyh0aXRsZSA9ICJEYWlseSBoYXJ2ZXN0IChsYikiLCAKICAgICAgIHggPSAiIiwKICAgICAgIHkgPSAiIiwKICAgICAgIGNvbG9yID0gInZlZ2V0YWJsZSIsCiAgICAgICBzaGFwZSA9ICJ2ZWdldGFibGUiKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoInRvbWF0b2VzIiA9ICJkYXJrcmVkIiwKICAgICAgICAgICAgICAgICAgICAgICAiYmVhbnMiID0gInNwcmluZ2dyZWVuNCIsCiAgICAgICAgICAgICAgICAgICAgICAgInBlYXMiID0gInllbGxvd2dyZWVuIiwKICAgICAgICAgICAgICAgICAgICAgICAienVjY2hpbmkiID0gImRhcmtncmVlbiIpKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIsCiAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSAKYGBgCgpOb3cgd2UgYW5pbWF0ZSB0aGUgcGxvdCwgdHJhbnNpdGluZyBvdmVyIHRpbWUgYnkgYGRhdGVgLgoKYGBge3IgdmVnMSwgZXZhbD1GQUxTRSwgZWNobz1GQUxTRX0KZGFpbHlfaGFydmVzdF9zdWJzZXQgJT4lIAogIGdncGxvdChhZXMoeCA9IGRhdGUsIAogICAgICAgICAgICAgeSA9IGRhaWx5X2hhcnZlc3RfbGIsCiAgICAgICAgICAgICBjb2xvciA9IHZlZ2V0YWJsZSwKICAgICAgICAgICAgIHNoYXBlID0gdmVnZXRhYmxlKSkgKwogIGdlb21fcG9pbnQoKSArCiAgbGFicyh0aXRsZSA9ICJEYWlseSBoYXJ2ZXN0IChsYikiLCAKICAgICAgIHggPSAiIiwKICAgICAgIHkgPSAiIiwKICAgICAgIGNvbG9yID0gInZlZ2V0YWJsZSIsCiAgICAgICBzaGFwZSA9ICJ2ZWdldGFibGUiKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoInRvbWF0b2VzIiA9ICJkYXJrcmVkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiYmVhbnMiID0gInNwcmluZ2dyZWVuNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBlYXMiID0gInllbGxvd2dyZWVuIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAienVjY2hpbmkiID0gImRhcmtncmVlbiIpKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIsCiAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArCiAgdHJhbnNpdGlvbl90aW1lKGRhdGUpCmBgYAoKYGBge3IsIGVjaG89RkFMU0V9CmRlY29yYXRlX2NodW5rKCJ2ZWcxIiwgZXZhbD1GQUxTRSkgJT4lIAogIGZsYWlyKCJ0cmFuc2l0aW9uX3RpbWUoZGF0ZSkiKQpgYGAKCmBgYHtyLCBldmFsPUZBTFNFLCBlY2hvPUZBTFNFfQphbmltX3NhdmUoInZlZzEuZ2lmIikKYGBgCgpgYGB7ciwgZWNobz1GQUxTRX0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoInZlZzEuZ2lmIikKYGBgCgpOb3csIGxldCdzIHRyeSBhZGRpbmcgc29tZSBvdGhlciBmZWF0dXJlczoKCiogS2VlcCBhIGxpdHRsZSBoaXN0b3J5IG9mIHRoZSBkYXRhIHZpYSBgc2hhZG93X3dha2UoKWAgIAoKKiBGYWRlIHRoZSBvbGQgZGF0YSBwb2ludHMgb3V0IHZpYSBgZXhpdF9mYWRlKClgCgoqIEFkZCBhIGRhdGUgc3VidGl0bGUgdXNpbmcgdGhlIGBmcmFtZV90aW1lYCB2YXJpYWJsZSBjcmVhdGVkIGZyb20gYHRyYW5zaXRpb25fdGltZSgpYC4KCmBgYHtyIHZlZzIsIGV2YWw9RkFMU0UsIGVjaG89RkFMU0V9CmRhaWx5X2hhcnZlc3Rfc3Vic2V0ICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBkYXRlLCAKICAgICAgICAgICAgIHkgPSBkYWlseV9oYXJ2ZXN0X2xiLAogICAgICAgICAgICAgY29sb3IgPSB2ZWdldGFibGUsCiAgICAgICAgICAgICBzaGFwZSA9IHZlZ2V0YWJsZSkpICsKICBnZW9tX3BvaW50KCkgKwogIGxhYnModGl0bGUgPSAiRGFpbHkgaGFydmVzdCAobGIpIiwgCiAgICAgICBzdWJ0aXRsZSA9ICJEYXRlOiB7ZnJhbWVfdGltZX0iLAogICAgICAgeCA9ICIiLAogICAgICAgeSA9ICIiLAogICAgICAgY29sb3IgPSAidmVnZXRhYmxlIiwKICAgICAgIHNoYXBlID0gInZlZ2V0YWJsZSIpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygidG9tYXRvZXMiID0gImRhcmtyZWQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJiZWFucyIgPSAic3ByaW5nZ3JlZW40IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGVhcyIgPSAieWVsbG93Z3JlZW4iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ6dWNjaGluaSIgPSAiZGFya2dyZWVuIikpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAidG9wIiwKICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsKICB0cmFuc2l0aW9uX3RpbWUoZGF0ZSkgKwogIHNoYWRvd193YWtlKHdha2VfbGVuZ3RoID0gLjMpICsKICBleGl0X2ZhZGUoKQpgYGAKCmBgYHtyLCBlY2hvPUZBTFNFfQpkZWNvcmF0ZV9jaHVuaygidmVnMiIsIGV2YWw9RkFMU0UpICU+JSAKICBmbGFpcigic2hhZG93X3dha2Uod2FrZV9sZW5ndGggPSAuMykiKSAlPiUgCiAgZmxhaXIoImV4aXRfZmFkZSgpIikgJT4lIAogIGZsYWlyKCdzdWJ0aXRsZSA9ICJEYXRlOiB7ZnJhbWVfdGltZX0iJykKYGBgCgpgYGB7ciwgZXZhbD1GQUxTRSwgZWNobz1GQUxTRX0KYW5pbV9zYXZlKCJ2ZWcyLmdpZiIpCmBgYAoKYGBge3IsIGVjaG89RkFMU0V9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJ2ZWcyLmdpZiIpCmBgYAoKIyMjIyBgdHJhbnNpdGlvbl9yZXZlYWwoKWAKClRoaXMgdHJhbnNpdGlvbiBhbGxvd3MgeW91IHRvIGxldCBkYXRhIGdyYWR1YWxseSBhcHBlYXIuIFdlIHdpbGwgc2hvdyBhbiBleGFtcGxlIG9mIGJ1aWxkaW5nIHVwIHRoZSBjdW11bGF0aXZlIGhhcnZlc3QgZGF0YSBvdmVyIGhhcnZlc3QgZGF0ZXMgdXNpbmcgdGhlIGBnYXJkZW5faGFydmVzdGAgZGF0YXNldC4gIFdlIHdpbGwgZm9sbG93IHRoZSBzdGVwcyBvdXRsaW5lZCBlYXJsaWVyIGZvciBjcmVhdGluZyBhbiBhbmltYXRlZCBwbG90LgoKRmlyc3Qgd2UgY3JlYXRlIGEgZGF0YXNldCBvZiBjdW11bGF0aXZlIGhhcnZlc3QgYnkgZGF0ZSBmb3IgYSBzdWJzZXQgb2YgdmVnZXRhYmxlcy4KCmBgYHtyfQpjdW1faGFydmVzdF9zdWJzZXQgPC0gZ2FyZGVuX2hhcnZlc3QgJT4lIAogIGZpbHRlcih2ZWdldGFibGUgJWluJSBjKCJ0b21hdG9lcyIsICJiZWFucyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICJwZWFzIiwgInp1Y2NoaW5pIikpICU+JSAKICBncm91cF9ieSh2ZWdldGFibGUsIGRhdGUpICU+JSAKICBzdW1tYXJpemUoZGFpbHlfaGFydmVzdF9sYiA9IHN1bSh3ZWlnaHQpKjAuMDAyMjA0NjIpICU+JSAKICBtdXRhdGUoY3VtX2hhcnZlc3RfbGIgPSBjdW1zdW0oZGFpbHlfaGFydmVzdF9sYikpCgpjdW1faGFydmVzdF9zdWJzZXQKYGBgCgpOZXh0LCB3ZSBjcmVhdGUgYSBzdGF0aWMgcGxvdCBvZiBjdW11bGF0aXZlIGhhcnZlc3QsIGNvbG9yaW5nIHRoZSBsaW5lcyBieSB2ZWdldGFibGUuCgpgYGB7cn0KY3VtX2hhcnZlc3Rfc3Vic2V0ICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBkYXRlLCAKICAgICAgICAgICAgIHkgPSBjdW1faGFydmVzdF9sYiwKICAgICAgICAgICAgIGNvbG9yID0gdmVnZXRhYmxlKSkgKwogIGdlb21fbGluZSgpICsKICBsYWJzKHRpdGxlID0gIkN1bXVsYXRpdmUgaGFydmVzdCAobGIpIiwgCiAgICAgICB4ID0gIiIsCiAgICAgICB5ID0gIiIsCiAgICAgICBjb2xvciA9ICJ2ZWdldGFibGUiKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoInRvbWF0b2VzIiA9ICJkYXJrcmVkIiwKICAgICAgICAgICAgICAgICAgICAgICAiYmVhbnMiID0gInNwcmluZ2dyZWVuNCIsCiAgICAgICAgICAgICAgICAgICAgICAgInBlYXMiID0gInllbGxvd2dyZWVuIiwKICAgICAgICAgICAgICAgICAgICAgICAienVjY2hpbmkiID0gImRhcmtncmVlbiIpKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIsCiAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKQpgYGAKCkFuZCBub3csIGFkZCBhbmltYXRpb24hIAoKYGBge3IgdmVnLWN1bTEsIGV2YWw9RkFMU0UsIGVjaG89RkFMU0V9CmN1bV9oYXJ2ZXN0X3N1YnNldCAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gZGF0ZSwgCiAgICAgICAgICAgICB5ID0gY3VtX2hhcnZlc3RfbGIsCiAgICAgICAgICAgICBjb2xvciA9IHZlZ2V0YWJsZSkpICsKICBnZW9tX2xpbmUoKSArCiAgbGFicyh0aXRsZSA9ICJDdW11bGF0aXZlIGhhcnZlc3QgKGxiKSIsIAogICAgICAgeCA9ICIiLAogICAgICAgeSA9ICIiLAogICAgICAgY29sb3IgPSAidmVnZXRhYmxlIikgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJ0b21hdG9lcyIgPSAiZGFya3JlZCIsCiAgICAgICAgICAgICAgICAgICAgICAgImJlYW5zIiA9ICJzcHJpbmdncmVlbjQiLAogICAgICAgICAgICAgICAgICAgICAgICJwZWFzIiA9ICJ5ZWxsb3dncmVlbiIsCiAgICAgICAgICAgICAgICAgICAgICAgInp1Y2NoaW5pIiA9ICJkYXJrZ3JlZW4iKSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiLAogICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIHRyYW5zaXRpb25fcmV2ZWFsKGRhdGUpCmBgYAoKCmBgYHtyLCBlY2hvPUZBTFNFfQpkZWNvcmF0ZV9jaHVuaygidmVnLWN1bTEiLCBldmFsPUZBTFNFKSAlPiUgCiAgZmxhaXIoInRyYW5zaXRpb25fcmV2ZWFsKGRhdGUpIikKYGBgCgpgYGB7ciwgZXZhbD1GQUxTRSwgZWNobz1GQUxTRX0KYW5pbV9zYXZlKCJ2ZWdjdW0xLmdpZiIpCmBgYAoKYGBge3IsIGVjaG89RkFMU0V9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJ2ZWdjdW0xLmdpZiIpCmBgYAoKQW5kIG5vdyBsZXQncyBkbyBhIGNvdXBsZSB0aGluZ3MgdG8gaW1wcm92ZSB0aGUgcGxvdDoKCiogUmVtb3ZlIHRoZSBsZWdlbmQgYW5kIGFkZCB0ZXh0IHRoYXQgc2hvd3MgdmVnZXRhYmxlIG5hbWUgb24gdGhlIHBsb3QgKEkgbG92ZSB0aGlzISkuIAoKKiBBZGQgZGF0ZSB0byB0aGUgc3VidGl0bGUuCgpgYGB7ciB2ZWctY3VtMiwgZXZhbD1GQUxTRSwgZWNobz1GQUxTRX0KY3VtX2hhcnZlc3Rfc3Vic2V0ICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBkYXRlLCAKICAgICAgICAgICAgIHkgPSBjdW1faGFydmVzdF9sYiwKICAgICAgICAgICAgIGNvbG9yID0gdmVnZXRhYmxlKSkgKwogIGdlb21fbGluZSgpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gdmVnZXRhYmxlKSkgKwogIGxhYnModGl0bGUgPSAiQ3VtdWxhdGl2ZSBoYXJ2ZXN0IChsYikiLCAKICAgICAgIHN1YnRpdGxlID0gIkRhdGU6IHtmcmFtZV9hbG9uZ30iLAogICAgICAgeCA9ICIiLAogICAgICAgeSA9ICIiLAogICAgICAgY29sb3IgPSAidmVnZXRhYmxlIikgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJ0b21hdG9lcyIgPSAiZGFya3JlZCIsCiAgICAgICAgICAgICAgICAgICAgICAgImJlYW5zIiA9ICJzcHJpbmdncmVlbjQiLAogICAgICAgICAgICAgICAgICAgICAgICJwZWFzIiA9ICJ5ZWxsb3dncmVlbiIsCiAgICAgICAgICAgICAgICAgICAgICAgInp1Y2NoaW5pIiA9ICJkYXJrZ3JlZW4iKSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogIHRyYW5zaXRpb25fcmV2ZWFsKGRhdGUpCmBgYAoKCmBgYHtyLCBlY2hvPUZBTFNFfQpkZWNvcmF0ZV9jaHVuaygidmVnLWN1bTIiLCBldmFsPUZBTFNFKSAlPiUgCiAgZmxhaXIoJ2xlZ2VuZC5wb3NpdGlvbiA9ICJub25lIicpICU+JSAKICBmbGFpcigiZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHZlZ2V0YWJsZSkpIikgJT4lIAogIGZsYWlyKCdzdWJ0aXRsZSA9ICJEYXRlOiB7ZnJhbWVfYWxvbmd9IicpCmBgYAoKYGBge3IsIGV2YWw9RkFMU0UsIGVjaG89RkFMU0V9CmFuaW1fc2F2ZSgidmVnY3VtMi5naWYiKQpgYGAKCmBgYHtyLCBlY2hvPUZBTFNFfQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygidmVnY3VtMi5naWYiKQpgYGAKCldlIGNvdWxkIGhhdmUgdXNlZCB0aGlzIHNhbWUgZGF0YSB3aXRoIGEgZGlmZmVyZW50IHR5cGUgb2YgdHJhbnNpdGlvbi4gSXQncyBhbHdheXMgZ29vZCB0byB0aGluayBhYm91dCB0aGUgcG9pbnQgeW91IGFyZSB0cnlpbmcgdG8gbWFrZSB3aXRoIHRoZSBhbmltYXRpb24uCgpgYGB7ciB2ZWctY3VtMywgZXZhbD1GQUxTRSwgZWNobz1GQUxTRX0KY3VtX2hhcnZlc3Rfc3Vic2V0ICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBkYXRlLCAKICAgICAgICAgICAgIHkgPSBjdW1faGFydmVzdF9sYiwKICAgICAgICAgICAgIGNvbG9yID0gdmVnZXRhYmxlKSkgKwogIGdlb21fbGluZSgpICsKICBsYWJzKHRpdGxlID0gIkN1bXVsYXRpdmUgaGFydmVzdCAobGIpIiwgCiAgICAgICBzdWJ0aXRsZSA9ICJWZWdldGFibGU6IHtjbG9zZXN0X3N0YXRlfSIsCiAgICAgICB4ID0gIiIsCiAgICAgICB5ID0gIiIsCiAgICAgICBjb2xvciA9ICJ2ZWdldGFibGUiKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoInRvbWF0b2VzIiA9ICJkYXJrcmVkIiwKICAgICAgICAgICAgICAgICAgICAgICAiYmVhbnMiID0gInNwcmluZ2dyZWVuNCIsCiAgICAgICAgICAgICAgICAgICAgICAgInBlYXMiID0gInllbGxvd2dyZWVuIiwKICAgICAgICAgICAgICAgICAgICAgICAienVjY2hpbmkiID0gImRhcmtncmVlbiIpKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgdHJhbnNpdGlvbl9zdGF0ZXModmVnZXRhYmxlKQpgYGAKCmBgYHtyLCBlY2hvPUZBTFNFfQpkZWNvcmF0ZV9jaHVuaygidmVnLWN1bTMiLCBldmFsPUZBTFNFKSAlPiUgCiAgZmxhaXIoJ3N1YnRpdGxlID0gIlZlZ2V0YWJsZToge2Nsb3Nlc3Rfc3RhdGV9IicpICU+JSAKICBmbGFpcigidHJhbnNpdGlvbl9zdGF0ZXModmVnZXRhYmxlKSIpCmBgYAoKYGBge3IsIGV2YWw9RkFMU0UsIGVjaG89RkFMU0V9CmFuaW1fc2F2ZSgidmVnY3VtMy5naWYiKQpgYGAKCmBgYHtyLCBlY2hvPUZBTFNFfQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygidmVnY3VtMy5naWYiKQpgYGAKCiMjIyBEZW1vIHZpZGVvCgpOb3cgd2F0Y2ggdGhlIGRlbW8gdmlkZW8hCgo8aWZyYW1lIHdpZHRoPSI1NjAiIGhlaWdodD0iMzE1IiBzcmM9Imh0dHBzOi8vd3d3LnlvdXR1YmUuY29tL2VtYmVkL1l5Zmk5NnhpQk1vIiBmcmFtZWJvcmRlcj0iMCIgYWxsb3c9ImFjY2VsZXJvbWV0ZXI7IGF1dG9wbGF5OyBjbGlwYm9hcmQtd3JpdGU7IGVuY3J5cHRlZC1tZWRpYTsgZ3lyb3Njb3BlOyBwaWN0dXJlLWluLXBpY3R1cmUiIGFsbG93ZnVsbHNjcmVlbj48L2lmcmFtZT4KCltWb2ljZXRocmVhZDogYGdnYW5pbWF0ZWAgZGVtb10oaHR0cHM6Ly92b2ljZXRocmVhZC5jb20vbXl2b2ljZS9jcmVhdGUvMTU2MTk4OTcpCgpgYGB7ciwgZWNobz1GQUxTRX0KZG93bmxvYWRfZmlsZSgKICBwYXRoID0gImdnYW5pbWF0ZV9kZW1vX25vX2NvZGUuUm1kIiwKICBidXR0b25fbGFiZWwgPSAiRG93bmxvYWQgZ2dhbmltYXRlIGRlbW8gZmlsZSAod2l0aG91dCBjb2RlKSIsCiAgYnV0dG9uX3R5cGUgPSAid2FybmluZyIsCiAgaGFzX2ljb24gPSBUUlVFLAogIGljb24gPSAiZmEgZmEtc2F2ZSIsCiAgc2VsZl9jb250YWluZWQgPSBGQUxTRQopCmBgYAoKYGBge3IsIGVjaG89RkFMU0V9CmRvd25sb2FkX2ZpbGUoCiAgcGF0aCA9ICJnZ2FuaW1hdGVfZGVtby5SbWQiLAogIGJ1dHRvbl9sYWJlbCA9ICJEb3dubG9hZCBnZ2FuaW1hdGUgZGVtbyBmaWxlICh3aXRoIGNvZGUpIiwKICBidXR0b25fdHlwZSA9ICJpbmZvIiwKICBoYXNfaWNvbiA9IFRSVUUsCiAgaWNvbiA9ICJmYSBmYS1zYXZlIiwKICBzZWxmX2NvbnRhaW5lZCA9IEZBTFNFCikKYGBgCgojIyMgUmVzb3VyY2VzCgoqIFtnZ2FuaW1hdGUgaW50cm8gc2xpZGVzXShodHRwczovL2dvb2Rla2F0LmdpdGh1Yi5pby9wcmVzZW50YXRpb25zLzIwMTktaXN1Z2ctZ2dhbmltYXRlLXNwb29reS9zbGlkZXMuaHRtbCkgYnkgS2F0aGVyaW5lIEdvb2RlIChzaGUgYW5pbWF0ZXMgYmF0cyBmbHlpbmchKQoKKiBbZ2dhbmltYXRlIGNoZWF0c2hlZXRdKGh0dHBzOi8vdWdvcHJvdG8uZ2l0aHViLmlvL3Vnb19yX2RvYy9wZGYvZ2dhbmltYXRlLnBkZikKCiogW2dnYW5pbWF0ZSBieSBUaG9tYXMgUGVkZXJzZW5dKGh0dHBzOi8vZ2l0aHViLmNvbS90aG9tYXNwODUvZ2dhbmltYXRlKSAtIHNjcm9sbCBkb3duIHRvIHRoZSBib3R0b20gIAoqIFtQZWRlcnNlbiBpbnRyb2R1Y3RvcnkgdmlnbmV0dGVdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9nZ2FuaW1hdGUvdmlnbmV0dGVzL2dnYW5pbWF0ZS5odG1sKSAtIGdpdmVzIGEgYnJpZWYgaW50cm8gdG8gd2hhdCBlYWNoIG9mIHRoZSBrZXkgZnVuY3Rpb25zIGRvICAKKiBbZ2dhbmltYXRlIHdpa2kgcGFnZV0oaHR0cHM6Ly9naXRodWIuY29tL3Rob21hc3A4NS9nZ2FuaW1hdGUvd2lraSkgLSBtb3N0IG9mIHRoaXMgaXMgY3VycmVudGx5IHVuZGVyIGRldmVsb3BtZW50IGJ1dCB0aGVyZSdzIHNvbWUgZ29vZCBleGFtcGxlcyAgCgoqIFtyb3BlbnNjaSBleGFtcGxlc10oaHR0cHM6Ly9naXRodWIuY29tL3JvcGVuc2NpbGFicy9sZWFybmdnYW5pbWF0ZSkKCiMjIyBTZXR0aW5nIHVwIGBnZ2FuaW1hdGVgCgpUaGlzIHBhY2thZ2UgKm1pZ2h0KiByZXF1aXJlIHNvbWUgZXh0cmEgc2V0dXAuCgoqIE1ha2Ugc3VyZSB5b3UgY2FuIGxvYWQgdGhlIGZvbGxvd2luZyBwYWNrYWdlczogYGdnYW5pbWF0ZWAsIGBnaWZza2lgLCBgdHJhbnNmb3JtcmAuIEZpcnN0LCB0cnkganVzdCBpbnN0YWxsaW5nIGBnZ2FuaW1hdGVgIGFuZCBzZWUgaWYgeW91IGNhbiBsb2FkIGFsbCB0aGUgb3RoZXIgcGFja2FnZXMgYWZ0ZXIgb25seSBpbnN0YWxsaW5nIHRoYXQgb25lLiBJZiBzbywgeW91IGFyZSBkb25lLiBJZiBub3QsIHRyeSBpbnN0YWxsaW5nIHRoZSBvdGhlciBwYWNrYWdlcy4gQWZ0ZXIgeW91IGluc3RhbGwgdGhlbSBhbGwsIFJFU1RBUlQgUlN0dWRpby4gSG9wZWZ1bGx5IHlvdSBoYXZlIHN1Y2Nlc3MgYXQgdGhhdCBwb2ludC4gSWYgbm90LCB0YWxrIHRvIG1lLiBJZiB5b3UgYXJlIHVzaW5nIE1hY2FsZXN0ZXIncyBzZXJ2ZXIsIHlvdSB3aWxsIGxpa2VseSBoYXZlIHRvIGRvIHRoZSBuZXh0IHN0ZXAuCgoqIElmIHlvdSB1c2UgTWFjYWxlc3RlcidzIHNlcnZlciwgeW91IHdpbGwgYWxtb3N0IHN1cmVseSBnZXQgYW4gZXJyb3Igd2hlbiB5b3UgdHJ5IHRvIGluc3RhbGwgYGdpZnNraWAuIFRoZSBlcnJvciB3aWxsIHNheSBzb21ldGhpbmcgYWJvdXQgbm90IGhhdmUgUlVTVCBhbmQgd2lsbCBkaXJlY3QgeW91IHRvIHRoZSBbUnVzdF0oaHR0cHM6Ly93d3cucnVzdC1sYW5nLm9yZy8pIHdlYnNpdGUuIENsaWNrIFtHZXR0aW5nIHN0YXJ0ZWRdKGh0dHBzOi8vd3d3LnJ1c3QtbGFuZy5vcmcvbGVhcm4vZ2V0LXN0YXJ0ZWQpLiAKCiAgLSBJbiB0aGUgKnRlcm1pbmFsKiwgdHJ5IHR5cGluZyBgcnVzdHVwIHVwZGF0ZWAuIElmIHRoYXQgaXMgc3VjY2Vzc2Z1bCwgeW91IGFyZSBkb25lLiBJZiBpdCB0ZWxscyB5b3Ugc29tZXRoaW5nIGFib3V0IG5vdCBoYXZpbmcgUnVzdCwgdGhlbiBnbyB0byB0aGUgbmV4dCBzdGVwLiAgCiAgLSBJZiB0aGUgcHJldmlvdXMgc3RlcCB3YXMgbm90IHN1Y2Nlc3NmdWwsIHRyeSBydW5uaW5nIHRoZSBmb2xsb3dpbmcgaW4gdGhlICp0ZXJtaW5hbCo6IGBjdXJsIC0tcHJvdG8gJz1odHRwcycgLS10bHN2MS4yIC1zU2YgaHR0cHM6Ly9zaC5ydXN0dXAucnMgfCBzaGAgKHRoaXMgaXMgZnJvbSB0aGUgQ2xpY2sgW0dldHRpbmcgc3RhcnRlZF0oaHR0cHM6Ly93d3cucnVzdC1sYW5nLm9yZy9sZWFybi9nZXQtc3RhcnRlZCkgUnVzdCBwYWdlIGFuZCBjb3VsZCBiZSBzbGlnaHRseSBvdXQgb2YgZGF0ZSAtIGdvIHRvIHRoYXQgcGFnZSB0byBhc3N1cmUgdGhlIGNvZGUgaXMgY29ycmVjdCkuIFRoaXMgbWF5IHJ1biBvbiBpdHMgb3duIG9yIGl0IG1heSBnaXZlIHlvdSBzb21lIG9wdGlvbnMuIEFsd2F5cyB0eXBlIHRoZSB5ZXMgb3B0aW9ucyBpbiB0aGUgdGVybWluYWwuICAKICAtIEFmdGVyIGluc3RhbGxpbmcsIGNsb3NlIHRoZSBzZXNzaW9uIChnbyB0byBob3VzZSBpY29uIGluIHRvcCByaWdodCkgYW5kIG9wZW4gYSBuZXcgc2Vzc2lvbi4gIAogIC0gTm93IGluc3RhbGwgdGhlIGdpZnNraSBsaWJyYXJ5LiBJZiB0aGF0IHJ1bnMgc3VjY2Vzc2Z1bGx5LCBjbG9zZSB0aGUgUiBzZXNzaW9uIGFuZCBvcGVuIGEgbmV3IG9uZS4gSWYgdGhhdCB3YXMgYWxsIHN1Y2Nlc3NmdWwsIHlvdSBzaG91bGQgYmUgYWJsZSB0byBjcmVhdGUgYW5pbWF0aW9ucyEKCiMjIyBZb3VyIHR1cm4hCgpVc2UgYW5pbWF0aW9uIHRvIHRlbGwgYW4gaW50ZXJlc3Rpbmcgc3Rvcnkgd2l0aCB0aGUgYHNtYWxsX3RyYWluc2AgZGF0YXNldCB0aGF0IGNvbnRhaW5zIGRhdGEgZnJvbSB0aGUgU05DRiAoTmF0aW9uYWwgU29jaWV0eSBvZiBGcmVuY2ggUmFpbHdheXMpLiBUaGVzZSBhcmUgVGlkeSBUdWVzZGF5IGRhdGEhIFJlYWQgbW9yZSBhYm91dCBpdCBbaGVyZV0oaHR0cHM6Ly9naXRodWIuY29tL3Jmb3JkYXRhc2NpZW5jZS90aWR5dHVlc2RheS90cmVlL21hc3Rlci9kYXRhLzIwMTkvMjAxOS0wMi0yNikuCgpgYGB7cn0Kc21hbGxfdHJhaW5zIDwtIHJlYWRfY3N2KCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vcmZvcmRhdGFzY2llbmNlL3RpZHl0dWVzZGF5L21hc3Rlci9kYXRhLzIwMTkvMjAxOS0wMi0yNi9zbWFsbF90cmFpbnMuY3N2IikgCmBgYAoKIyMgQ3JlYXRpbmcgYW4gYXBwIHdpdGggYHNoaW55YAoKSW4gdGhpcyBzZWN0aW9uLCB3ZSB3aWxsIGxlYXJuIGhvdyB0byBjcmVhdGUgYSBTaGlueSBBcHAuIFNoaW55IEFwcHMgYXJlIGFwcGxpY2F0aW9ucyB0aGF0IGFsbG93IHRoZSB1c2VyIHRvIGludGVyYWN0IG9yIG1ha2UgY2hhbmdlcyB0byBncmFwaHMgb3IgdGFibGVzLiBZb3Ugd2lsbCBsZWFybiBob3cgdG8gY3JlYXRlIHRoZW0gaW4gUiBhbmQgaG93IHRvIGRlcGxveSB0aGVtIG9uIHlvdXIgb3duIFtzaGlueWFwcHMuaW9dKHNoaW55YXBwcy5pbykgd2VicGFnZS4gU2VlIGV4YW1wbGVzIG9mIHNvbWUgYXBwcyBbaGVyZV0oaHR0cHM6Ly9zaGlueS5yc3R1ZGlvLmNvbS9nYWxsZXJ5LykuCgojIyMgQ29uY2VwdCBNYXAKClRoZSBjb25jZXB0IG1hcCBiZWxvdyBpbGx1c3RyYXRlcyB0aGUga2V5IGNvbXBvbmVudHMgb2YgYSBzaGlueSBhcHAgYW5kIGhvdyB0aGV5IHJlbGF0ZSB0byBvbmUgYW5vdGhlci4gV2Ugd2lsbCBnbyB0aHJvdWdoIG1vcmUgZGV0YWlsIGR1cmluZyB0aGUgdHV0b3JpYWwuCgohW10oLi4vLi4vaW1hZ2VzL3NoaW55X2NvbmNlcHRfbWFwLnBuZykKCiMjIyBTbGlkZXMKCkkgYW0gZG9pbmcgdGhpcyB0dXRvcmlhbCBhIGJpdCBkaWZmZXJlbnRseSB0aGFuIEkndmUgZG9uZSBvdGhlciB0dXRvcmlhbHMuIEkgYW0gZ29pbmcgdG8gd2FsayB5b3UgdGhyb3VnaCB0aGUgY3JlYXRpb24gb2YgYSBzaGlueSBhcHAgYnkgZm9sbG93aW5nIHRoZSBpbnRyb190b19zaGlueSBzbGlkZXMgb24gbXkgR2l0SHViIHBhZ2UuIFlvdSBjYW4gZG93bmxvYWQgdGhlIHNsaWRlcyBiZWxvdy4gSSB3aWxsIGluY2x1ZGUgc2hvcnQgc2NyZWVuIGNhcHR1cmVzIHRvIGlsbHVzdHJhdGUgaG93IHRvIGRvIGVhY2ggcGFydCBvbiB5b3VyIG93bi4KCmBgYHtyLCBlY2hvPUZBTFNFfQpkb3dubG9hZF9maWxlKAogIHBhdGggPSAiLi4vMDVfaW50cm9fdG9fc2hpbnkvaW50cm9fdG9fc2hpbnkuaHRtbCIsCiAgYnV0dG9uX2xhYmVsID0gIkRvd25sb2FkIGludHJvX3RvX3NoaW55IHNsaWRlcyIsCiAgYnV0dG9uX3R5cGUgPSAiaW5mbyIsCiAgaGFzX2ljb24gPSBUUlVFLAogIGljb24gPSAiZmEgZmEtc2F2ZSIsCiAgc2VsZl9jb250YWluZWQgPSBGQUxTRQopCmBgYAoKIyMjIEdldHRpbmcgdGhlIGZpbGVzCgpUbyBiZWdpbiwgeW91IGFyZSBnb2luZyB0byBjb3B5IGV2ZXJ5dGhpbmcgZnJvbSBteSBHaXRIdWIgcmVwbyB0byB5b3VyIG93biBHaXRIdWIgcmVwby4gWW91IGRvIHRoaXMgYnkgKmZvcmtpbmcqLiBGcm9tIHlvdXIgR2l0SHViIGFjY291bnQsIHNlYXJjaCBmb3IgbWluZTogbGxlbmR3YXkvaW50cm9fdG9fc2hpbnkuIE9uY2UgdGhlcmUsIGNsaWNrIHRoZSBmb3JrIGJ1dHRvbi4gVGhlbiwgYWxsIG15IGZpbGVzIHdpbGwgYmUgaW4gYSByZXBvIG9mIHRoZSBzYW1lIG5hbWUgb24geW91ciBHaXRIdWIgcGFnZS4KCiFbXSguLi9naWZzLzAwX2ZvcmsubXA0KXt3aWR0aD0iOTAlIn0KCkZyb20gdGhlcmUsIGNsb25lIHRoZSByZXBvIGFuZCBjcmVhdGUgYSBuZXcgcHJvamVjdCBpbiBSIFN0dWRpby4KCk9uY2UgeW91J3ZlIGRvbmUgdGhhdCwgeW91IGNhbiBhY2Nlc3MgYWxsIHRoZSBmaWxlcyBmcm9tIHlvdXIgY29tcHV0ZXIuIEFzIHlvdSBtYWtlIGNoYW5nZXMsIHlvdSBjYW4gY29tbWl0IGFuZCBwdXNoIHRoZW0gb3V0IHRvIHlvdXIgb3duIEdpdEh1YiBhY2NvdW50LCBpZiB5b3UnZCBsaWtlLgoKIyMjIENyZWF0aW5nIGFuIGFwcAoKQ3JlYXRpbmcgYSBTaGlueSBhcHAgaXMgZGlmZmVyZW50IGZyb20gd2hhdCB3ZSd2ZSBkb25lIHNvIGZhciBpbiB0aGlzIGNsYXNzLiBPbmUgb2YgdGhlIGJpZ2dlc3QgY2hhbmdlcywgaXMgdGhhdCB3ZSdsbCBiZSB3b3JraW5nIGZyb20gLlIgZmlsZXMsIHJhdGhlciB0aGFuIC5SbWQgZmlsZXMuIEluIC5SIGZpbGVzLCAqZXZlcnl0aGluZyogaXMgcmVhZCBhcyBSIGNvZGUuIFNvLCBpdCdzIGxpa2Ugb25lIGJpZyBSIGNvZGUgY2h1bmsuIElmIHlvdSB3YW50IHRvIG1ha2UgY29tbWVudHMsIHlvdSBuZWVkIHRvIHVzZSB0aGUgcG91bmQvaGFzaHRhZyBzeW1ib2wsIGAjYC4KCkxldCdzIHN0YXJ0IGJ5IG9wZW5pbmcgdGhlIGBiYXNpY19hcHBfdGVtcGxhdGUuUmAgZmlsZSBpbiB0aGUgYGFwcF9maWxlc2AgZm9sZGVyLiBTaW5jZSB5b3UndmUgY2xvbmVkIHRoZSByZXBvLCB0aGlzIHdpbGwgYmUgb25lIG9mIHRoZSBmaWxlcyBpbiB5b3VyIHByb2plY3QgZm9sZGVyLiBNYWtlIHN1cmUgeW91IGhhdmUgdGhlIHByb2plY3Qgb3BlbiBmaXJzdCEgT3BlbiB0aGUgZmlsZSBhbmQgY2xpY2sgUnVuIEFwcC4gVGhpcyBpcyBhICpyZWFsbHkqIGJvcmluZyBhcHAgLSB0aGVyZSBpcyBub3RoaW5nIHRoZXJlISBCdXQsIGl0IGlzIGEgZ3JlYXQgc3RhcnRpbmcgcG9pbnQgYmVjYXVzZSBpdCBnaXZlcyB5b3UgYW4gb3V0bGluZSBvZiB3aGF0IHlvdSBuZWVkIGluIG9yZGVyIHRvIG1ha2UgeW91ciBhcHAgd29yay4KCiFbXSguLi9naWZzLzAyX2Jhc2ljX2FwcC5tcDQpe3dpZHRoPSI5MCUifQoKQmVmb3JlIGdldHRpbmcgaW50byBhIGxvdCBvZiBkZXRhaWxzLCBsZXQncyBhZGQgYSBsaXR0bGUgYml0IHRvIHRoZSBhcHAuIEF0IHRoZSB0b3Agb2YgdGhlIGZpbGUsIGxvYWQgdGhlIGB0aWR5dmVyc2VgIGFuZCBgYmFieW5hbWVzYCBsaWJyYXJpZXMgYW5kIGFkZCBzb21lIHRleHQgYmV0d2VlbiBxdW90ZXMgaW5zaWRlIHRoZSBgZmx1aWRQYWdlKClgIGZ1bmN0aW9uLlJ1biB0aGUgYXBwLiBZb3UgY2FuIGNoZWNrIHRoYXQgeW91IGRpZCB0aGlzIHJpZ2h0IGJ5IGxvb2tpbmcgaW4gdGhlIGBiYXNpY19hcHBfYWRkX21vcmUuUmAgZmlsZS4KCiFbXSguLi9naWZzLzAzX2Jhc2ljX2FkZC5tcDQpe3dpZHRoPSI5MCUifQoKTm93LCBsZXQncyBtb3ZlIG9uIHRvIGNyZWF0aW5nIGEgbW9yZSBpbnRlcmVzdGluZyBhcHAuIFRoZSBnb2FsIGlzIHRvIGNyZWF0ZSBhIFNoaW55IGFwcCBmb3IgbXkga2lkcyB0byBleHBsb3JlIHRoZSBgYmFieW5hbWVzYCBkYXRhc2V0ISBSZW1lbWJlciwgdGhhdCdzIHRoZWlyIGZhdm9yaXRlLgoKUmVxdWlyZW1lbnRzOiAKCiogU2hvdyB0aGUgbnVtYmVyIG9mIGJhYmllcyB3aXRoIGEgZ2l2ZW4gbmFtZSBvdmVyIHRpbWUuICAKKiBBbGxvdyB0aGUgdXNlciB0byB0eXBlIGEgc2luZ2xlIG5hbWUuIChJZGVhbGx5LCBpdCBzaG91bGQgZ2l2ZSBhIG1lc3NhZ2UgaWYgdGhlcmUgYXJlIG5vIHJlY29yZHMgb2YgdGhlIG5hbWUgdGhhdCB3YXMgdHlwZWQsIGJ1dCB3ZSB3b24ndCB3b3JyeSBhYm91dCB0aGF0IG5vdy4pICAKKiBBbGxvdyB0aGUgdXNlciB0byBjaG9vc2UgYSByYW5nZSBvZiB5ZWFycyB0aGV5IHdvdWxkIGxpa2UgdG8gZGlzcGxheS4gCiogQWxsb3cgdGhlIHVzZXIgdG8gZmlsdGVyIGJ5IHNleC4KCiMjIyMgVGhlIGRldGFpbHMKCkhvdyBkbyB3ZSBkbyB0aGlzPwoKU2V0dXA6CgoxLiBDcmVhdGUgZm9sZGVyIChlaXRoZXIgY3JlYXRlIGEgbmV3IHByb2plY3Qgb3IgcHV0IGl0IGluIHRoZSBwcm9qZWN0IGZvbGRlciB5b3UgZm9ya2VkIGFuZCBjbG9uZWQgZnJvbSBteSByZXBvKS4gR2l2ZSB0aGUgZm9sZGVyIGEgbmFtZSB0aGF0IGRlc2NyaWJlcyB0aGUgYXBwLiAgCjIuIE9wZW4gbmV3ICpSIFNjcmlwdCogZmlsZSAoKm5vdCogLlJtZCBmaWxlKSAgCjMuIENvcHkgYW5kIHBhc3RlIGNvZGUgZnJvbSB0aGUgYmFzaWNfYXBwX2FkZF9tb3JlLlIgZmlsZSAgCjQuIFNhdmUgdGhlIFIgU2NyaXB0IGZpbGUgYXMgYXBwLlIgaW50byB0aGUgZm9sZGVyIHlvdSBqdXN0IGNyZWF0ZWQuICAKCiFbXSguLi9naWZzLzA0X2NyZWF0ZV9hcHBfZmlsZS5tcDQpe3dpZHRoPSI5MCUifQoKRnVuIHBhcnRzOgoKNS4gQWRkIGB1aWAgY29tcG9uZW50cyAgCjYuIEFkZCBgc2VydmVyYCBjb21wb25lbnRzICAKNy4gRGVwbG95IHRvIHNoaW55YXBwcy5pbyB0byBzaGFyZSB3aXRoIHRoZSB3b3JsZCEKCkxldCdzIGxlYXJuIG1vcmUgYWJvdXQgdGhlc2UgZnVuIHBhcnRzIQoKIyMjIyBXaGF0IGlzIGFuIGFwcC5SIGZpbGU/CgpMaWtlIHdlJ3ZlIGFscmVhZHkgc2VlbiBpbiB0aGUgc2FtcGxlIGFwcCBmaWxlcywgdGhlc2UgY29udGFpbiB0d28ga2V5IGNvbXBvbmVudHM6CgoqIGB1aWA6IHRoZSB1c2VyIGludGVyZmFjZS4gVGhpcyBpcyB0aGUgd2VicGFnZSB0aGF0IHlvdXIgdXNlciB3aWxsIGludGVyYWN0IHdpdGguIERvbid0IHdvcnJ5LCB5b3UgZG9uJ3QgbmVlZCB0byBrbm93IGhvdyB0byB3cml0ZSBodG1sISBUaGUgYXBwIHdpbGwgZG8gdGhhdCBmb3IgeW91ISAoQWx0aG91Z2ggaWYgeW91IHdhbnQgdG8sIHRoZXJlIGFyZSBvcHBvcnR1bml0aWVzIHRvIHVzZSBodG1sLikKCiogYHNlcnZlcmA6IHRoZSBjb21wdXRlciBwYXJ0LiBXaGF0IHNob3VsZCB0aGUgY29tcHV0ZXIvc2VydmVyIGRvIHdpdGggeW91ciBpbnB1dHMgYXMgdGhlIHVzZXIgY2hhbmdlcyB0aGVtLiBUaGlzIHNlY3Rpb24gd2lsbCBoYXZlIFIgY29kZSBpbiBpdCwgbW9yZSBsaWtlIHdlJ3JlIHVzZWQgdG8gLi4uIHNvcnQgb2YuCgpJIGFsd2F5cyBrZWVwIHRoZXNlIG5hbWVzIGFzIHRoZSBkZWZhdWx0LiBUaGUgbGFzdCBjaHVuayBvZiBjb2RlIGF0IHRoZSBib3R0b20sIGBzaGlueUFwcCh1aSA9IHVpLCBzZXJ2ZXIgPSBzZXJ2ZXIpYCwgd2lsbCBjb21waWxlIGV2ZXJ5dGhpbmcgdG9nZXRoZXIgdG8gcmVzdWx0IGluIHRoZSBpbnRlcmFjdGl2ZSB3ZWJwYWdlLgoKV2Ugd2lsbCBhZGQgZGlmZmVyZW50IGAqSW5wdXQoKWAgYW5kIGAqT3V0cHV0KClgIGZ1bmN0aW9ucyB0byB0aGUgYHVpYC4gIAoKKiBUaGUgYCpJbnB1dCooKWAgZnVuY3Rpb25zIGNvbGxlY3QgaW5wdXRzIGZyb20gdGhlIHVzZXIuICAKKiBUaGUgYCpPdXRwdXQoKWAgZnVuY3Rpb25zIHdvcmsgd2l0aCB0aGUgYHJlbmRlciooKWAgZnVuY3Rpb25zIGluIHRoZSBgc2VydmVyYCBwb3J0aW9uIHRvIHRvIGFkZCBSIG91dHB1dCB0byB0aGUgVUkuIAoKSGF2ZSB0aGUgW2NoZWF0c2hlZXRdKGh0dHBzOi8vc2hpbnkucnN0dWRpby5jb20vaW1hZ2VzL3NoaW55LWNoZWF0c2hlZXQucGRmKSBvcGVuIGF0IGFsbCB0aW1lcyEgSXQgaXMgZXh0cmVtZWx5IGhlbHBmdWwuCgojIyMjIGAqSW5wdXQoKWAgZnVuY3Rpb25zCgpUaGUgYCpJbnB1dCooKWAgZnVuY3Rpb25zIGNvbGxlY3QgaW5wdXRzIGZyb20gdGhlIHVzZXIuIFRoZSB2YXJpb3VzIHR5cGVzIGFyZSBsaXN0ZWQgb24gdGhlIHJpZ2h0LWhhbmQgc2lkZSBvZiB0aGUgZmlyc3QgcGFnZSBvZiB0aGUgY2hlYXRzaGVldC4gWW91IHdpbGwgbGlzdCBhbGwgdGhlIGAqSW5wdXQoKWAgZnVuY3Rpb25zIHlvdSB3YW50IHRvIHVzZSB3aXRoIHRoZWlyIGFjY29tcGFueWluZyBhcmd1bWVudHMgaW5zaWRlIHRoZSBgZmx1aWRQYWdlKClgIGZ1bmN0aW9uIGluIHRoZSBgdWlgIHBvcnRpb24uIFNlcGFyYXRlIHRoZSBgKklucHV0KClgIGZ1bmN0aW9ucyB3aXRoIGNvbW1hcy4gSW4gdGhlIGJhc2ljX2FwcF90ZW1wbGF0ZSwgYWRkIHRocmVlIGlucHV0cyBpbnNpZGUgdGhlIGBmbHVpZFBhZ2UoKWAgZnVuY3Rpb24uIEJlIHN1cmUgdG8gc2VwYXJhdGUgdGhlbSB3aXRoIGNvbW1hcy4KCkluIGFsbCB0aGUgYCpJbnB1dCgpYCBmdW5jdGlvbnMsIHRoZSBmaXJzdCB0d28gYXJndW1lbnRzIGFyZSB0aGUgc2FtZTogYGlucHV0SWRgIGlzIGhvdyB5b3Ugd2lsbCBjYWxsIHRoaXMgaW5wdXQgaW4gdGhlIGBzZXJ2ZXJgIHBvcnRpb24gbGF0ZXIsIGBsYWJlbGAgaXMgaG93IHRoaXMgd2lsbCBhY3R1YWxseSBiZSBsYWJlbGVkIGluIHlvdXIgVUkuIEVhY2ggZnVuY3Rpb24gaGFzIHNvbWUgYWRkaXRpb25hbCBhcmd1bWVudHMgZGVwZW5kaW5nIHdoYXQgeW91IHdhbnQgdG8gZG8uCgpJbiB0aGUgYGFwcC5SYCBmaWxlIHlvdSBqdXN0IGNyZWF0ZWQsIGFkZCB0aHJlZSBpbnB1dHMgaW5zaWRlIHRoZSBgZmx1aWRQYWdlKClgIGZ1bmN0aW9uLiBCZSBzdXJlIHRvIHNlcGFyYXRlIHRoZW0gd2l0aCBjb21tYXMuCgoqIGBzbGlkZXJJbnB1dCgpYCB0byBjaG9vc2UgdGhlIHN0YXJ0IGFuZCBlbmQgeWVhciBmb3IgdGhlIGV2ZW50dWFsIGdyYXBoLiAgCiogYHRleHRJbnB1dCgpYCB0byB3cml0ZSBhIG5hbWUuICAKKiBgc2VsZWN0SW5wdXQoKWAgdG8gY2hvb3NlIGEgc2V4LgoKT25jZSB5b3UgY29tcGxldGUgYWxsIHRoZSBuZWNlc3NhcnkgYXJndW1lbnRzLCBydW4geW91ciBhcHAuIE1ha2Ugc3VyZSB5b3UgY2FuIGVudGVyIGFuZCBtb3ZlIHRoaW5ncyBhcm91bmQgYXMgZXhwZWN0ZWQuIFRoZXJlIHdvbid0IGJlIGEgZ3JhcGggeWV0IGJlY2F1c2Ugd2UgaGF2ZW4ndCBjcmVhdGVkIGl0LiBZb3UgY2FuIGNoZWNrIHlvdXIgcmVzdWx0cyBieSBsb29raW5nIGF0IHRoZSBgYmFieW5hbWVzX2FwcF9zdGVwMS5SYCBmaWxlIChJJ3ZlIGFsc28gYWRkZWQgYSBjb3VwbGUgZXh0cmEgdGhpbmdzIC0gc2VlIGlmIHlvdSBjYW4gZmlndXJlIG91dCB3aGF0IHRoZXkgZG8pLgoKIyMjIyBgKk91dHB1dCgpYCBmdW5jdGlvbnMKCmAqT3V0cHV0KClgIGZ1bmN0aW9ucyBpbiB0aGUgYHVpYCBwb3J0aW9uIHdvcmsgd2l0aCB0aGUgYHJlbmRlciooKWAgZnVuY3Rpb25zIGluIHRoZSBgc2VydmVyYCBwb3J0aW9uIHRvIHRvIGFkZCBSIG91dHB1dCB0byB0aGUgVUkuIFRoZSBgKk91dHB1dCgpYCBmdW5jdGlvbnMgYXJlIGxpc3RlZCBpbiB0aGUgYm90dG9tIGNlbnRlciBwYXJ0IG9mIHRoZSBmaXJzdCBwYWdlIG9mIHRoZSBjaGVhdHNoZWV0LiAKCkFsbCB0aGUgYCpPdXRwdXQoKWAgZnVuY3Rpb25zIGhhdmUgdGhlIHNhbWUgZmlyc3QgYXJndW1lbnQsIGBvdXRwdXRJZGAsIHdoaWNoIGlzIHVzZWQgaG93IHlvdSB3aWxsIGNhbGwgdGhpcyBvdXRwdXQgaW4gdGhlIGBzZXJ2ZXJgIHBvcnRpb24gbGF0ZXIgKGxpa2UgdGhlIGBpbnB1dElkYCBpbiB0aGUgYCpJbnB1dCgpYCBmdW5jdGlvbnMpLgoKTm93LCBhZGQgYSBgcGxvdE91dHB1dCgpYCB0byB0aGUgYGZsdWlkUGFnZSgpYCBmdW5jdGlvbi4gUnVuIHRoZSBhcHAgd2l0aCB0aGUgb3V0cHV0LiBZb3UgY2FuIGNoZWNrIHlvdXIgd29yayBieSBsb29raW5nIGF0IHRoZSBgYmFieW5hbWVzX2FwcF9zdGVwMi5SYCBmaWxlLiBOb3RpY2UgdGhhdCBub3RoaW5nIHJlYWxseSBjaGFuZ2VzLiBUaGluayBvZiB0aGlzIG91dHB1dCBhcyBhIHBsYWNlaG9sZGVyLiBTbywgaXQga25vd3MgdGhlcmUgaXMgZ29pbmcgdG8gYmUgYSBwbG90IGluIHRoZSBVSSwgYnV0IHRoZSBkZXRhaWxzIG9mIHdoYXQgdGhlIHBsb3Qgd2lsbCBsb29rIGxpa2UgYW5kIHRoZSBSIGNvZGUgdG8gY3JlYXRlIGl0IHdpbGwgYmUgaW4gdGhlIHNlcnZlciBwb3J0aW9uLiBMZXQncyB0YWxrIGFib3V0IHRoYXQhCgojIyMjIFVzaW5nIGByZW5kZXIqKClgIGZ1bmN0aW9ucyAKCkluIHRoZSBzZXJ2ZXIgcG9ydGlvbiBvZiB0aGUgY29kZSwgd2Ugd2lsbCB1c2UgYHJlbmRlciooKWAgZnVuY3Rpb25zIHdpdGggUiBjb2RlIHRvIGNvbW11bmljYXRlIGhvdyB0byB1c2UgdGhlIGlucHV0IHBpZWNlcyBhbG9uZyB3aXRoIHRoZSBSIGNvZGUgdG8gY3JlYXRlIHRoZSBkZXNpcmVkIG91dHB1dC4gVGhlIGByZW5kZXIqKClgIGZ1bmN0aW9uIHlvdSB1c2Ugd2lsbCBkZXBlbmQgb24gdGhlIGRlc2lyZWQgb3V0cHV0LiBUaGUgYm90dG9tIGNlbnRlciBvZiB0aGUgY2hlYXRzaGVldCBzaG93cyBob3cgYCpPdXRwdXQoKWAgYW5kIGByZW5kZXIqKClgIGZ1bmN0aW9ucyBjb25uZWN0LgoKSW4gZ2VuZXJhbCwgdGhlIGBzZXJ2ZXJgIHNlY3Rpb24gb2YgY29kZSB3aWxsIGxvb2sgc29tZXRoaW5nIGxpa2UgdGhpczoKCmBgYHtyIGV2YWw9RkFMU0V9CnNlcnZlciA8LSBmdW5jdGlvbihpbnB1dCwgb3V0cHV0KSB7CiAgb3V0cHV0JG91dHB1dElkX29mX2ludGVyZXN0IDwtIHJlbmRlciooewpSIGNvZGUgdGhhdCBjcmVhdGVzIHRoZSBvdXRwdXQgYW5kIGNhbGxzIHZhcmlvdXMgaW5wdXQkSW5wdXRJZCdzCiAgfSkKfQpgYGAKClNvLCBpZiBpbnNpZGUgdGhlIGB1aWAgcGFydCwgd2UgZGlkIGBwbG90T3V0cHV0KG91dHB1dElkID0gInRpbWVwbG90IilgLCB0aGVuIGluIHRoZSBgc2VydmVyYCBwYXJ0LCB3ZSB3b3VsZCB1c2UgYG91dHB1dCR0aW1lcGxvdCA8LSByZW5kZXJQbG90KHsuLi59KWAgYW5kIHB1dCBpbiBkZXRhaWxlZCBSIGNvZGUgaW4gcGxhY2Ugb2YgdGhlIGAuLi5gLiBUbyByZWZlcmVuY2UgdGhlIGlucHV0cyB3ZSBjcmVhdGVkIGluIHRoZSBgdWlgLCB3ZSB1c2UgYGlucHV0JGlucHV0SURfbmFtZWAuIFNvLCBmb3IgZXhhbXBsZSwgaWYgd2UgaGFkIGFuIGAqSW5wdXQoKWAgd2l0aCBgaW5wdXRJZCA9ICJ5ZWFycyJgLCB3ZSB3b3VsZCB1c2UgYGlucHV0JHllYXJzYCBpbiB0aGUgYHNlcnZlcmAgcG9ydGlvbi4KCk5vdywgc2luY2Ugd2UgYXJlIGludGVyZXN0ZWQgaW4gY3JlYXRpbmcgYSBwbG90LCBhZGQgdGhlIGByZW5kZXJQbG90KClgIGZ1bmN0aW9uIGluc2lkZSB0aGUgYHNlcnZlcmAgcG9ydGlvbiBvZiB0aGUgY29kZS4gUmVmZXJlbmNlIHRoZSBpbnB1dHMgeW91J3ZlIGFscmVhZHkgY3JlYXRlZCBpbiBwcmV2aW91cyBwYXJ0cyBhbmQgdXNlIGBmaWx0ZXIoKWAgYW5kIGBnZ3Bsb3QoKWAgdG8gcmVuZGVyIHRoZSBkZXNpcmVkIGludGVyYWN0aXZlIHBsb3QuIFJ1biB0aGUgYXBwIGFuZCBjaGVjayB5b3VyIHdvcmsgYnkgbG9va2luZyBhdCB0aGUgYGJhYnluYW1lc19hcHBfYmFzaWMuUmAgZmlsZSBvciBpbiB0aGUgY29kZSBjaHVuayBiZWxvdyAoc28gZG9uJ3QgbG9vayBhaGVhZCwgaWYgeW91IHdhbnQgdG8gdHJ5IGl0IG91dCBvbiB5b3VyIG93biEpLiBJIGFsc28gYWRkZWQgYSBzdWJtaXQgYnV0dG9uIHRvIHRoZSBVSSBzbyBldmVyeXRoaW5nIGlzIHVwZGF0ZWQgYXQgdGhlIHNhbWUgdGltZSwgb25seSB3aGVuIHRoZSBidXR0b24gaXMgY2xpY2tlZC4gWW91IHNob3VsZCB0cnkgYWRkaW5nIHRoYXQsIHRvby4KCkJlbG93LCBJIGhhdmUgYWxzbyBpbmNsdWRlZCBhbGwgdGhlIFIgY29kZSB1c2VkIHRvIGNyZWF0ZSB0aGUgYXBwLiBJIGhhdmUgaGlnaGxpZ2h0ZWQgdGhlIGBpbnB1dElkYCBhbmQgYG91dHB1dElkYCBzaW1pbGFybHkgaW4gdGhlIGB1aWAgYW5kIGBzZXJ2ZXJgIHBvcnRpb25zIHRvIGRyYXcgYXR0ZW50aW9uIHRvIHdoZXJlIHRoZXkgYXJlIHJlZmVyZW5jZWQuIAoKYGBge3IgYXBwLWNvZGUsIGV2YWw9RkFMU0UsIGVjaG89RkFMU0V9CmxpYnJhcnkoc2hpbnkpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGJhYnluYW1lcykKCnVpIDwtIGZsdWlkUGFnZSgKICBzbGlkZXJJbnB1dChpbnB1dElkID0gInllYXJzIiwgCiAgICAgICAgICAgICAgbGFiZWwgPSAiWWVhciBSYW5nZSIsCiAgICAgICAgICAgICAgbWluID0gMTg4MCwgCiAgICAgICAgICAgICAgbWF4ID0gMjAxOSwgCiAgICAgICAgICAgICAgdmFsdWUgPSBjKDE4ODAsMjAxOSksCiAgICAgICAgICAgICAgc2VwID0gIiIpLAogIHRleHRJbnB1dCgibmFtZSIsIAogICAgICAgICAgICAiTmFtZSIsIAogICAgICAgICAgICB2YWx1ZSA9ICIiLCAKICAgICAgICAgICAgcGxhY2Vob2xkZXIgPSAiTGlzYSIpLAogIHNlbGVjdElucHV0KCJzZXgiLCAKICAgICAgICAgICAgICAiU2V4IiwgCiAgICAgICAgICAgICAgY2hvaWNlcyA9IGxpc3QoRmVtYWxlID0gIkYiLCBNYWxlID0gIk0iKSksCiAgc3VibWl0QnV0dG9uKHRleHQgPSAiQ3JlYXRlIG15IHBsb3QhIiksCiAgcGxvdE91dHB1dChvdXRwdXRJZCA9ICJ0aW1lcGxvdCIpCikKCnNlcnZlciA8LSBmdW5jdGlvbihpbnB1dCwgb3V0cHV0KSB7CiAgb3V0cHV0JHRpbWVwbG90IDwtIHJlbmRlclBsb3QoewogIGJhYnluYW1lcyAlPiUgCiAgICBmaWx0ZXIobmFtZSA9PSBpbnB1dCRuYW1lLCAKICAgICAgICAgICBzZXggPT0gaW5wdXQkc2V4KSAlPiUgCiAgICBnZ3Bsb3QoKSArCiAgICBnZW9tX2xpbmUoYWVzKHggPSB5ZWFyLCB5ID0gbikpICsKICAgIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBpbnB1dCR5ZWFycykgKwogICAgdGhlbWVfbWluaW1hbCgpCiAgfSkKfQoKc2hpbnlBcHAodWkgPSB1aSwgc2VydmVyID0gc2VydmVyKQpgYGAKCmBgYHtyLCBlY2hvPUZBTFNFfQpkZWNvcmF0ZV9jaHVuaygiYXBwLWNvZGUiLCBldmFsID0gRkFMU0UpICU+JSAKICBmbGFpcignInllYXJzIicpICU+JSAKICBmbGFpcigiaW5wdXQkeWVhcnMiKSAlPiUgCiAgZmxhaXIoJyJuYW1lIicsIGJhY2tncm91bmQgPSAib3JhbmdlIikgJT4lIAogIGZsYWlyKCJpbnB1dCRuYW1lIiwgYmFja2dyb3VuZCA9ICJvcmFuZ2UiKSAlPiUgCiAgZmxhaXIoJyJzZXgiJywgYmFja2dyb3VuZCA9ICJsaWdodGdyZWVuIikgJT4lIAogIGZsYWlyKCJpbnB1dCRzZXgiLCBiYWNrZ3JvdW5kID0gImxpZ2h0Z3JlZW4iKSAlPiUgCiAgZmxhaXIoJyJ0aW1lcGxvdCInLCBiYWNrZ3JvdW5kID0gImxpZ2h0Ymx1ZSIpICU+JSAKICBmbGFpcigib3V0cHV0JHRpbWVwbG90IiwgYmFja2dyb3VuZCA9ICJsaWdodGJsdWUiKQpgYGAKCiMjIyMgUHVibGlzaGluZyB5b3VyIGFwcAoKMS4gSW5zdGFsbCBhbmQgbG9hZCB0aGUgYHJzY29ubmVjdGAgbGlicmFyeSBpbiB5b3VyIFIgU3R1ZGlvIHNlc3Npb24uIAoyLiBSZWdpc3RlciBhdCBbaHR0cHM6Ly93d3cuc2hpbnlhcHBzLmlvL2FkbWluLyMvc2lnbnVwXShodHRwczovL3d3dy5zaGlueWFwcHMuaW8vYWRtaW4vIy9zaWdudXApLiAgCjMuIE9uY2UgeW91IGFyZSBsb2dnZWQgaW4gdG8gc2hpbnlhcHBzLmlvLCBnbyB0byBBY2NvdW50IC0tPiBUb2tlbnMgYW5kIGNsaWNrIHRoZSBTaG93IGJ1dHRvbi4gIAo0LiBDb3B5IGFuZCBwYXN0ZSB0aGUgY29kZSBpbnRvIHRoZSBjb25zb2xlIGluIFIuIFRoaXMgd2lsbCBjb25uZWN0IHlvdXIgYWNjb3VudCB0byBSIFN0dWRpby4gCjUuIFdoZW4geW91IGNyZWF0ZSBhbiBhcHAsIHNhdmUgaXQgYXMgYGFwcC5SYCBpbiBhIGZvbGRlci4gSXQgKipNVVNUKiogYmUgbmFtZWQgYGFwcC5SYC4gSW4gdGhlIGBhcHAuUmAgZmlsZSwgbG9hZCBhbGwgbGlicmFyaWVzIHlvdSB1c2UgaW4geW91ciBjb2RlLiBBbHNvIHRyeSBub3QgdG8gaGF2ZSBleHRyYSBsaWJyYXJpZXMgb3IgaXQgd2lsbCB0YWtlIGxvbmdlciB0byBnZXQgaXQgb24gdGhlIHdlYnNpdGUuIEFueSBkYXRhIHRoYXQgeW91ciBhcHAgdXNlcyBuZWVkcyB0byBiZSByZWFkIGluIHdpdGhpbiB0aGUgYXBwLiBJZiB0aGUgZGF0YSBhcmUgbG9jYWwgdG8geW91ciBjb21wdXRlciwgeW91IG5lZWQgdG8gaGF2ZSB0aGUgZGF0YSBpbiB0aGUgc2FtZSBmb2xkZXIgYXMgdGhlIGFwcC4gICAgCjYuIFJ1biB0aGUgYXBwLiBJbiB0aGUgdXBwZXIgcmlnaHQtaGFuZCBjb3JuZXIsIHRoZXJlIGlzIGFuIG9wdGlvbiB0byBwdWJsaXNoIHRoZSBhcHAuIENsaWNrIG9uIHRoYXQuIEl0IHdpbGwgdGFrZSBhIGJpdCBvZiB0aW1lIHRvIGRvIGl0IHRoZSBmaXJzdCB0aW1lLiBPbmNlIHB1Ymxpc2hlZCwgeW91IGNhbiBnbyB0byB0aGUgYXBwIHZpYSB0aGUgd2VicGFnZSBwcm92aWRlZC4KClRoZSBpbnN0cnVjdGlvbnMgYXJlIHNldCBvdXQgaW4gbW9yZSBkZXRhaWwgW2hlcmVdKGh0dHBzOi8vc2hpbnkucnN0dWRpby5jb20vYXJ0aWNsZXMvc2hpbnlhcHBzLmh0bWwpLiAKCllvdSBjYW4gc2VlIHRoZSBleGFtcGxlIG9mIG15IHNhbXBsZSBhcHAgYmVmb3JlIGl0IHdhcyBwdWJsaXNoZWQgaW4gdGhlIHNoaW55X2FwcCBmb2xkZXIgb2YgYWxsIG15IGZpbGVzIChub3RlIHRoYXQgdGhlIGByc2Nvbm5lY3RgIGZvbGRlciBpcyBhZGRlZCBhZnRlciBwdWJsaXNoaW5nIHRoZSBhcHApLiBUaGUgcHVibGlzaGVkIGFwcCBpcyBbaGVyZV0oaHR0cHM6Ly9saXNhbGVuZHdheS5zaGlueWFwcHMuaW8vc2hpbnlfYXBwLykuIAoKCiMjIyBEZW1vIHZpZGVvCgpZb3UgY2FuIHdhdGNoIGEgdmlkZW8gb2YgbWUgY3JlYXRpbmcgdGhpcyBzYW1lIHNoaW55IGFwcCB0byBiZXR0ZXIgc2VlIGhvdyBhbGwgdGhlIHBpZWNlcyBmaXQgdG9nZXRoZXIuIFRoaXMgdmlkZW8gaXMgYSBiaXQgbG9uZywgYnV0IHlvdSBjYW4gYWx3YXlzIHNraXAgdGhyb3VnaCBzb21lIHBhcnRzLgoKPGlmcmFtZSB3aWR0aD0iNTYwIiBoZWlnaHQ9IjMxNSIgc3JjPSJodHRwczovL3d3dy55b3V0dWJlLmNvbS9lbWJlZC9ha19OSkNWckpYWSIgZnJhbWVib3JkZXI9IjAiIGFsbG93PSJhY2NlbGVyb21ldGVyOyBhdXRvcGxheTsgY2xpcGJvYXJkLXdyaXRlOyBlbmNyeXB0ZWQtbWVkaWE7IGd5cm9zY29wZTsgcGljdHVyZS1pbi1waWN0dXJlIiBhbGxvd2Z1bGxzY3JlZW4+PC9pZnJhbWU+CgpbVm9pY2V0aHJlYWQ6IENyZWF0aW5nIGEgc2hpbnkgYXBwIGRlbW9dKGh0dHBzOi8vdm9pY2V0aHJlYWQuY29tL3NoYXJlLzE1NjYxMTE1LykKCiMjIyBSZXNvdXJjZXMKCltQYXJ0IEldKGh0dHBzOi8vc2hpbnkucnN0dWRpby5jb20vdHV0b3JpYWwvKSBvZiBHYXJyZXR0IEdyb2xlbXVuZCdzIHR1dG9yaWFsIC0gdGhyb3VnaCB0aGUgc2hpbnlhcHBzLmlvIHNlY3Rpb24uIAoKW1NoaW55IGNoZWF0c2hlZXRdKGh0dHBzOi8vc2hpbnkucnN0dWRpby5jb20vaW1hZ2VzL3NoaW55LWNoZWF0c2hlZXQucGRmKQoKW0xpc2EncyBJbnRybyB0byBTaGlueV0oaHR0cHM6Ly9naXRodWIuY29tL2xsZW5kd2F5L2ludHJvX3RvX3NoaW55KSBHaXRIdWIgcGFnZQoKIyMjIFlvdXIgdHVybiEKClRyeSBhZGRpbmcgc29tZXRoaW5nIG1vcmUgdG8gdGhlIGFwcC4gSGVyZSBhcmUgYSBjb3VwbGUgaWRlYXMuCgoxLiBSZXR1cm4gYSBtZXNzYWdlIGlmIHRoZSB1c2VyIGVudGVycyBhIG5hbWUgaW5jb3JyZWN0bHkuIEJ5IHRoZSB3YXksIHRoaXMgd2lsbCBoYXBwZW4gaWYgc29tZW9uZSBlbnRlcnMgYSBuYW1lIHdpdGggYWxsIGxvd2VyY2FzZSBsZXR0ZXJzLCB0b28uIEhvdyBjb3VsZCB5b3Ugc29sdmUgdGhhdCBwcm9ibGVtPwoKMi4gQWxsb3cgdGhlIHVzZXIgdG8gZW50ZXIgbW9yZSB0aGFuIG9uZSBuYW1lIGFuZCBjb21wYXJlIHRoZSBncmFwaHMgZWl0aGVyIGJ5IGNvbG9yaW5nIGRpZmZlcmVudCBsaW5lcyBvciB1c2luZyBmYWNldGluZy4gIAoKMy4gQWxsb3cgdGhlIHVzZXIgdG8gY2hhbmdlIHNvbWV0aGluZyBhYm91dCB0aGUgZ3JhcGgsIGxpa2UgdGhlIGNvbG9yIG9mIHRoZSBsaW5lIG9yIHRoZSB0eXBlIG9mIGxpbmUuCgoK