Set up

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

Welcome to your first tutorial for this class, COMP/STAT 112: Introduction to Data Science! As you work through the different sections, there will be videos for you to watch (both embedded YouTube videos and links to the videos on Voicethread), files for you to download, and exercises for you to work through. The solutions to the exercises are usually provided, but in order to get the most out of these tutorials, you should work through the exercises and only look at the solutions if you get really stuck. You could also work through the exercises in your own R Markdown file in order to keep the results permanently. If you do that, start the file with the three code chunks I talk about below. Then copy and paste the questions into your document and put your solutions in R code chunks.

If you haven’t done so already, please go through the R Basics document.

When you start your own document, you should have the following three code chunks at the top of your R Markdown file:

  1. Options that control what happens to the R code chunks.
knitr::opts_chunk$set(echo = TRUE, message=FALSE, warning=FALSE)
  1. Libraries that are used and other settings, like a theme you would like to use throughout the document. If you have not yet installed the libraries you are going to use, you will first have to install them. Go to the Packages tab (top of lower right box) and choose Install. You can then list the packages you would like to install. Alternatively, you can use the install.packages() function in the console and write the name of each of the packages you want to install. Some packages (like my gardenR package) needs to be installed in a special way using the install_github() function in the remotes library - uncomment (delete the hashtags from the front) those two lines of code to install the library. Then either delete those two lines or comment them again. You only need to install packages once, although you will need to re-install them if you upgrade to a new version of R. You need to load them with the library() statements each time you use them. There is a good analogy with lights: installing the package is like putting the light in the socket, loading the package is like turning the light on.
library(tidyverse)         # for graphing and data cleaning
library(lubridate)         # for working with dates
library(palmerpenguins)    # for palmer penguin data
# library(remotes)        # for installing package from GitHub
# remotes::install_github("llendway/gardenR") # run if package is not already installed
library(gardenR)           # for Lisa's garden data
theme_set(theme_minimal()) # my favorite ggplot theme
  1. Load data that will be used. Data from packages can be loaded using the data() function. Data outside of a package can be loaded in different ways depending where it is and what type of data it is. Later in the course, we will learn different functions that can be used to read in data from other places.
# Palmer Penguins data from palmerpenguins library
data("penguins")

# Lisa's garden data from gardenR library
data("garden_harvest")

Motivation

Before jumping into teaching you some Data Science skills in R, I want to give you some motivation. I picked three graphs I’ve recently seen on Twitter. These are all responses to #TidyTuesday which you’ll be participating in very soon! Read more about it here if you’re curious. There are many definitions of Data Science but I broadly like to think of it as using data to tell a story. These three graphs are just a small sample of doing just that.

One of my favorite Data Visualizers on Twitter:

One of my former students (and your preceptor!):

A #TidyTuesday newcomer:

Learning goals

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

  • Construct the “Five Named Graphs” using ggplot2 functions.
  • Add labels to graphs.
  • Change the theme of a graph.
  • Interpret or explain the graph you created.
  • Use the six main dplyr functions to begin “wrangling” data.
  • Pipe (%>%) together a sequence of dplyr functions to answer a question.
  • Combine dplyr verbs and ggplot() functions to wrangle and plot data.

Data

We will use two different datasets throughout this tutorial.

Palmer Penguins

The Palmer Penguins dataset is from the palmerpenguins library. The data we will use is called penguins. You can read about it within R by typing ?penguins in the console.

Let’s do some basic exploration of the data. The code below uses the dim() function to find the dimensions of the dataset - the number of rows and columns.

dim(penguins)
## [1] 344   8

And we use the head() function to view the first 6 rows of the data.

head(penguins)

Lisa’s Garden Data

The garden_harvest data contains data that I have collected from my personal garden in the summer of 2020. You can view the original google sheet here. Each row in the data is a “harvest” for a variety of a vegetable. So, vegetables might have multiple rows on a day, especially if they are things I eat twice a day (lettuce) or there are many different varieties of the vegetable (tomatoes).

I fondly refer to my garden as the “Jungle Garden” because by the end of the summer all the plants are creeping out of their beds and it can be quite the adventure walking through it. Take a look at the video below for an in-depth tour of the garden and details around how I collect the data.

Voicethread: Jungle Garden tour

Let’s also get an overview of this dataset.

Use the dim() function to find the number of cases and variables in the dataset.

Use the glimpse() function to show the first few cases of each of the variables and see the type of variable.

Creating graphs with ggplot()

Now, let’s get ready to plot some data! The concept map below provides an overview of the functions you will be learning, how they relate to one another, and what they do.

First, watch the video below that introduces the ggplot() syntax. You can download the slides. They will open in a web browser, and you can press the letter “p” to go to presentation mode and see my notes that go along with them.

Voicethread: Intro to ggplot()

Next, watch the video below that walks through some examples in R Studio. You can practice along with me by downloading the R Markdown file and working through the problems. If you do that, you will likely get somewhat different results than you see in the video when using the garden_harvest data because I made the videos when I was still in the midst of collecting data :)

Voicethread: ggplot() demo

Lastly, watch this short video about common mistakes. Hopefully you won’t make them, but admittedly I sometimes still do.

Voicethread: ggplot() mistakes

More with ggplot()!

I couldn’t resist giving a few more tips and tricks for creating beautiful plots using ggplot()!

View the video below:

Voicethread: More with ggplot()

And follow along with the code!

Resources

Your turn!

Now you have the tools you need to begin creating your own plots. As you work through these exercises, it will be helpful to have the Data Visualization with ggplot2 cheatsheet open. Find the cheatsheet here or, from within R Studio, go to Help –> Cheatsheets and click on Data Visualization with ggplot2.

Exercise 1a: Scatterplots

Use the penguins data to create a scatterplot of bill_length_mm (x-axis) vs. bill_depth_mm (y-axis). I have started the code for you. How would do describe the relationship?

penguins %>% 
  ggplot(   (x =    , 
             y =    )) +
  geom_()

Exercise 1b: Scatterplots

Now use the code you wrote in the previous exercise but color the points by species. How does this change how you described the relationship before?

CHALLENGE: Scatterplots

Now use the code you wrote in the previous exercise but make the points smaller and more transparent.

Exercise 2a: Histograms

Create a histogram of the flipper_length_mm.

Exercise 2b: Histograms

Add a facet to the previous histogram so there is a different histogram for each species. Make it so there is one column of plots. How would you compare the distributions?

Exercise 3a: Barplots

Create a barplot that shows the number of penguins for each year. Fill in the bars with the color lightblue.

CHALLENGE: Barplots

The code below creates a new dataset called tomatoes. Use the tomatoes dataset to create a barplot that shows the number of days that each tomato variety has been harvested. Make the bars horizontal, fill them in with the color tomato4 , order them from most to least (hint: use fct_infreq() and fct_rev()). Also give the plot nice labels.

tomatoes <- garden_harvest %>% 
  filter(vegetable == "tomatoes") 

Exercise 4: Boxplots

Use boxplots to compare the flipper_length_mm by species. Make the boxplots horizontal. How does this graph compare to the faceted histogram you made above? What are the strengths and weaknesses of each type of graph.

Exercise 5a: Line graphs

The code below creates a dataset (tomatoes_wt_date) that has the weight in grams of tomatoes (daily_wt_g) for each date. Use that to create a linegraph of the weight of tomatoes harvested each day.

tomatoes_wt_date <- garden_harvest %>% 
  filter(vegetable == "tomatoes") %>% 
  group_by(date) %>% 
  summarize(daily_wt_g = sum(weight))

Exercise 5b: Line graphs

The code below creates a dataset (tomato_variety_daily) that has the weight in grams of each variety of tomato (daily_wt_g) for each date. Use that to create a linegraph of the weight of tomatoes harvested each day, where there is a separate line for each variety, in a different color. What are some ways you might improve this graph?

tomato_variety_daily <- garden_harvest %>% 
  filter(vegetable == "tomatoes") %>% 
  group_by(date, variety) %>% 
  summarize(daily_wt_g = sum(weight))

Wrangling data with dplyr functions

Next, you will learn how to wrangle and manipulate data using six dplyr functions. There are many other functions we can use (and we will!) but these six will get us pretty far, especially when combined. The concept map below shows the six functions I will introduce and what they are used for.

First, watch the video below that introduces the dplyr functions. Again, you can download the slides. They will open in a web browser, and you can press the letter “p” to go to presentation mode and see my notes that go along with them.

Voicethread: Intro to dplyr

To recap, the six main dplyr verbs are summarized below.

Images from R Studio Cheatsheets: https://rstudio.com/resources/cheatsheets/

The main dplyr functions … illustrated

Here, I illustrate (I will not pretend to be an artist) some of the dplyr functions to highlight their main uses … and hopefully make you smile. In the made up dataset, called data (I know, it’s a terrible name), there are three variables: pet is the type of pet the student owns - a cat, a dog, or a fish (they all own a pet and only one in this dataset); n_classes is the number of classes the student is taking in this quarter; and hours_hw is the number of hours the student spends doing homework in a week. Descriptions are found below each illustration.

The raw data

We choose variables with select()

We add variables (keeping the same number of rows) with mutate(). Inside the mutate() function we need to tell it the details of how to compute the new variable.

We choose rows with filter()

We order rows with arrange()

We group data with group_by(), which changes the data “internally” but won’t make it look different. If you find yourself saying “for each” to describe somethig you want to do, you will probably want to use group_by() first.

We often use summarize() after a group_by(). This will add a new variable and decrease the number of rows based on how we summarize the data. Like mutate(), we also need to give the details of how to compute the new variable inside the function.

We can also group_by() more than one variable which will group observations by each combination of values of those variables.

Here is an example of using a summarize() after a group_by() that includes two variables. See the extra section on group_by() and summarize() for more information.

Useful functions and operators

This table shows the logical operators often used with the filter() verb.

Operator Meaning
== Equal to
> Greater than
< Less than
>= Greater than or equal to
<= Less than or equal to
!= Not equal to
%in% in
is.na is a missing value (NA)
!is.na is not a missing value
& and
| or

The table below shows common functions you would use when you add a variable using mutate(). Find more in the dplyr cheatsheet linked in Resources.

Function Meaning
+, -, *, /, ^ Arithmetic operations
>, <, etc. Logical operators (see above)
ifelse() Used to create binary variable from non-binary
lag() Offset elements by 1
cumsum() cumulative sum()

The table below shows common functions you would use when you add a summary variable using summarize(). Find more in the dplyr cheatsheet linked in Resources. With many of these functions, you can add na.rm = TRUE to remove missing values.

Function Meaning
n() number of rows
mean() mean
median() median
sum() sum
sd() standard deviation
IQR() interquartile range

Demo video

Next, watch the video below that walks through some examples in R Studio. Just like with the ggplot() material, you can practice the dplyr problems along with me by downloading the R Markdown file and working through them. If you do that, you will likely get somewhat different results than you see in the video when using the garden_harvest data because the data has changed since I made the video. At the time of the video, I was still collecting data from the garden :)

Voicethread: dplyr demo

A special note about the group_by() function

In the demo video, I don’t think I did enough to emphasize the grouping behavior and how it differs depending on which function comes after it. I highlight some key behaviors and give some recommendations below:

  • When group_by() is followed by a summarize(), the data will be grouped one level higher than in the group_by(). For example, in the code below the group_vars() function tells us by which variables the data are grouped. In the first set of code, they are grouped by vegetable in the end; but in the second, they are grouped by date in the end. This also means that if there is only one grouping variable, after a summarize(), the data are no longer grouped. This is illustrated in the third piece of code (note that it outputs character(0) which means there isn’t one).
garden_harvest %>% 
  group_by(vegetable, date) %>% 
  summarize(tot_harvest = sum(weight)) %>% 
  group_vars()
## [1] "vegetable"
garden_harvest %>% 
  group_by(date, vegetable) %>% 
  summarize(tot_harvest = sum(weight)) %>% 
  group_vars()
## [1] "date"
garden_harvest %>% 
  group_by(date) %>% 
  summarize(tot_harvest = sum(weight)) %>% 
  group_vars()
## character(0)
  • Using mutate() after a group_by() does not change the grouping. In the code below, we still see that the data are grouped by both vegetable and date.
garden_harvest %>% 
  group_by(vegetable, date) %>% 
  mutate(tot_harvest = sum(weight)) %>% 
  group_vars()
## [1] "vegetable" "date"
  • The ungroup() function removes all grouping as seen in the two pieces of code below.
garden_harvest %>% 
  group_by(vegetable, date) %>% 
  summarize(tot_harvest = sum(weight)) %>% 
  ungroup() %>% 
  group_vars()
## character(0)
garden_harvest %>% 
  group_by(vegetable, date) %>% 
  mutate(tot_harvest = sum(weight)) %>% 
  ungroup() %>% 
  group_vars()
## character(0)
  • It is important to pay close attention to how data are grouped after using group_by() followed by another function. The behavior of the grouping depends on which function follows the group_by(). Use the group_vars() function to check. You can always ungroup() and then use group_by() again or add a group_by() to explicitly assure the data are grouped in the way you intend them to be.

Resources

Your turn!

Exercise 1: select()

Select vegetable, date, and weight from the garden_harvest data. I have started the code for you below.

garden_harvest #What do I need to put here?
  select()

Exercise 2a: mutate()

Add a variable for weight in kilograms, weight_kg. One kilogram is 1000 grams. I started the code below.

garden_harvest #What do I need to put here?
  mutate()

Exercise 2b: mutate()

Keep the weight_kg variable from the previous problem and also add a variable to the garden_harvest data called day_of_week that returns the day of the week. HINT: Use the function wday() and add an argument to that function that is label=TRUE.

Exercise 3a: filter()

Filter the garden_harvest data to observations that have weights less than 50 grams.

Exercise 3b: filter()

Filter the garden_harvest data to peas and beans with weights larger than 40 grams.

Exercise 4a: arrange()

Order the observations in the garden_harvest data from largest to smallest weight.

Exercise 4b: arrange()

Order the observations in the garden_harvest data from largest to smallest weight on each date.

Exercise 5a: summarize()

Find the total weight in grams and how many rows of data are in the garden_harvest data.

Exercise 5b: summarize() with group_by()

Find the total weight in grams harvested for each date. After doing to summarize(), how are the data grouped?

Exercise 6: combining dplyr verbs

I love tomatoes. Well, truthfully, I love things made out of tomatoes - spaghetti sauce, salsa, soups, and even ketchup. I always want to know which variety of tomato is most productive. In this exercise, start with the garden_harvest data, filter to tomatoes, find the total weight for each variety, compute a new variable to convert the weights from grams to pounds, and lastly sort the data from largest to smallest total weight in pounds. Which variety is best? Is there any information missing? Think about (and check!) how your data are grouped each step of the way.

Exercise 7: combining dplyr verbs and ggplot()

I’m curious if there are certain days during the week where I harvest more or less. In this exercise, start with the garden_harvest data, find the daily harvest in grams for each date, create two new variables: 1. the daily harvest in pounds and 2. day of the week, plot the data so for each day of the week (on the y-axis) a boxplot of the daily harvest in pounds is created.

Exercise solutions

Exercise 1a: Scatterplots

penguins %>% 
  ggplot(aes(x = bill_length_mm, 
             y = bill_depth_mm)) +
  geom_point()

Exercise 1b: Scatterplots

penguins %>% 
  ggplot(aes(x = bill_length_mm, 
             y = bill_depth_mm,
             color = species)) +
  geom_point()

CHALLENGE: Scatterplots

penguins %>% 
  ggplot(aes(x = bill_length_mm, 
             y = bill_depth_mm,
             color = species)) +
  geom_point(alpha = .5, size = .5)

Exercise 2a: Histograms

penguins %>% 
  ggplot(aes(flipper_length_mm)) +
  geom_histogram()

Exercise 2b: Histograms

penguins %>% 
  ggplot(aes(flipper_length_mm)) +
  geom_histogram() +
  facet_wrap(vars(species), ncol = 1)

Exercise 3a: Barplots

penguins %>% 
  ggplot(aes(x=year)) +
  geom_bar(fill = "lightblue")

CHALLENGE: Barplots

tomatoes %>% 
  ggplot(aes(y=fct_rev(fct_infreq(variety)))) +
  geom_bar(fill = "tomato4") +
  labs(title = "Tomatoes", 
       subtitle = "# of days each variety has been harvested",
       x = "",
       y = "")

Exercise 4: Boxplots

penguins %>% 
  ggplot(aes(x = flipper_length_mm, y = species)) +
  geom_boxplot()

Exercise 5a: Line graphs

tomatoes_wt_date %>% 
  ggplot(aes(x = date, y = daily_wt_g)) +
  geom_line()

Exercise 5b: Line graphs

tomato_variety_daily %>% 
  ggplot(aes(x = date, y = daily_wt_g, color = variety)) +
  geom_line()

Exercise 1: `select()

garden_harvest %>% 
  select(vegetable, date, weight)

Exercise 2a: mutate()

garden_harvest %>% 
  mutate(weight_kg = weight/1000)

Exercise 2b: mutate()

garden_harvest %>% 
  mutate(weight_kg = weight/1000,
         day_of_week = wday(date, label = TRUE))

Exercise 3a: filter()

garden_harvest %>%
  filter(weight < 50)

Exercise 3b: filter()

garden_harvest %>% 
  filter(vegetable %in% c("peas", "beans"),
         weight > 40)

Exercise 4a: arrange()

garden_harvest %>% 
  arrange(desc(weight))

Exercise 4b: arrange()

garden_harvest %>% 
  arrange(date, desc(weight))

Exercise 5a: summarize()

garden_harvest %>% 
  summarize(nrows = n(),
            total_weight = sum(weight))

Exercise 5b: summarize() with group_by()

garden_harvest %>% 
  group_by(date) %>% 
  summarize(total_weight = sum(weight))

To examine how data are grouped:

garden_harvest %>% 
  group_by(date) %>% 
  summarize(total_weight = sum(weight)) %>% 
  group_vars()
## character(0)

Exercise 6: combining dplyr verbs

garden_harvest %>% 
  filter(vegetable == "tomatoes") %>% 
  group_by(variety) %>% 
  summarize(total_weight_g = sum(weight)) %>% 
  mutate(total_weight_lb = total_weight_g*0.00220462) %>% 
  arrange(desc(total_weight_lb))

Exercise 7: combining dplyr verbs and ggplot()

garden_harvest %>% 
  group_by(date) %>% 
  summarize(daily_harvest_g = sum(weight)) %>% 
  mutate(daily_harvest_lb = daily_harvest_g*0.00220462,
         day_of_week = wday(date, label=TRUE)) %>% 
  ggplot(aes(x = daily_harvest_lb, y = day_of_week)) + 
  geom_boxplot()

LS0tCnRpdGxlOiAnYGdncGxvdCgpYCBhbmQgYGRwbHlyYCB0dXRvcmlhbCcKb3V0cHV0OiAKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIGRmX3ByaW50OiBwYWdlZAogICAgY29kZV9kb3dubG9hZDogdHJ1ZQotLS0KCiMjIFNldCB1cCB7I3NldHVwfQoKVG8gZmluZCBvdGhlciB0dXRvcmlhbHMgZm9yIHRoaXMgY2xhc3MsIGdvIHRvIHRoZSBtYWluIHdlYnNpdGUsIFtodHRwczovL2RzMTEyLWxlbmR3YXkubmV0bGlmeS5hcHAvXShodHRwczovL2RzMTEyLWxlbmR3YXkubmV0bGlmeS5hcHAvKS4KCldlbGNvbWUgdG8geW91ciBmaXJzdCB0dXRvcmlhbCBmb3IgdGhpcyBjbGFzcywgQ09NUC9TVEFUIDExMjogKkludHJvZHVjdGlvbiB0byBEYXRhIFNjaWVuY2UqISBBcyB5b3Ugd29yayB0aHJvdWdoIHRoZSBkaWZmZXJlbnQgc2VjdGlvbnMsIHRoZXJlIHdpbGwgYmUgdmlkZW9zIGZvciB5b3UgdG8gd2F0Y2ggKGJvdGggZW1iZWRkZWQgWW91VHViZSB2aWRlb3MgYW5kIGxpbmtzIHRvIHRoZSB2aWRlb3Mgb24gVm9pY2V0aHJlYWQpLCBmaWxlcyBmb3IgeW91IHRvIGRvd25sb2FkLCBhbmQgZXhlcmNpc2VzIGZvciB5b3UgdG8gd29yayB0aHJvdWdoLiBUaGUgc29sdXRpb25zIHRvIHRoZSBleGVyY2lzZXMgYXJlIHVzdWFsbHkgcHJvdmlkZWQsIGJ1dCBpbiBvcmRlciB0byBnZXQgdGhlIG1vc3Qgb3V0IG9mIHRoZXNlIHR1dG9yaWFscywgeW91IHNob3VsZCB3b3JrIHRocm91Z2ggdGhlIGV4ZXJjaXNlcyBhbmQgb25seSBsb29rIGF0IHRoZSBzb2x1dGlvbnMgaWYgeW91IGdldCByZWFsbHkgc3R1Y2suIFlvdSBjb3VsZCBhbHNvIHdvcmsgdGhyb3VnaCB0aGUgZXhlcmNpc2VzIGluIHlvdXIgb3duIFIgTWFya2Rvd24gZmlsZSBpbiBvcmRlciB0byBrZWVwIHRoZSByZXN1bHRzIHBlcm1hbmVudGx5LiBJZiB5b3UgZG8gdGhhdCwgc3RhcnQgdGhlIGZpbGUgd2l0aCB0aGUgdGhyZWUgY29kZSBjaHVua3MgSSB0YWxrIGFib3V0IGJlbG93LiBUaGVuIGNvcHkgYW5kIHBhc3RlIHRoZSBxdWVzdGlvbnMgaW50byB5b3VyIGRvY3VtZW50IGFuZCBwdXQgeW91ciBzb2x1dGlvbnMgaW4gUiBjb2RlIGNodW5rcy4gCgpJZiB5b3UgaGF2ZW4ndCBkb25lIHNvIGFscmVhZHksIHBsZWFzZSBnbyB0aHJvdWdoIHRoZSBbUiBCYXNpY3NdKGh0dHBzOi8vci1iYXNpY3MubmV0bGlmeS5hcHAvKSBkb2N1bWVudC4gCgpXaGVuIHlvdSBzdGFydCB5b3VyIG93biBkb2N1bWVudCwgeW91IHNob3VsZCBoYXZlIHRoZSBmb2xsb3dpbmcgdGhyZWUgY29kZSBjaHVua3MgYXQgdGhlIHRvcCBvZiB5b3VyIFIgTWFya2Rvd24gZmlsZToKCjEuIE9wdGlvbnMgdGhhdCBjb250cm9sIHdoYXQgaGFwcGVucyB0byB0aGUgUiBjb2RlIGNodW5rcy4KCmBgYHtyIHNldHVwfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UpCmBgYAoKMi4gTGlicmFyaWVzIHRoYXQgYXJlIHVzZWQgYW5kIG90aGVyIHNldHRpbmdzLCBsaWtlIGEgdGhlbWUgeW91IHdvdWxkIGxpa2UgdG8gdXNlIHRocm91Z2hvdXQgdGhlIGRvY3VtZW50LiBJZiB5b3UgaGF2ZSBub3QgeWV0IGluc3RhbGxlZCB0aGUgbGlicmFyaWVzIHlvdSBhcmUgZ29pbmcgdG8gdXNlLCB5b3Ugd2lsbCBmaXJzdCBoYXZlIHRvIGluc3RhbGwgdGhlbS4gR28gdG8gdGhlIFBhY2thZ2VzIHRhYiAodG9wIG9mIGxvd2VyIHJpZ2h0IGJveCkgYW5kIGNob29zZSBJbnN0YWxsLiBZb3UgY2FuIHRoZW4gbGlzdCB0aGUgcGFja2FnZXMgeW91IHdvdWxkIGxpa2UgdG8gaW5zdGFsbC4gQWx0ZXJuYXRpdmVseSwgeW91IGNhbiB1c2UgdGhlIGBpbnN0YWxsLnBhY2thZ2VzKClgIGZ1bmN0aW9uIGluIHRoZSBjb25zb2xlIGFuZCB3cml0ZSB0aGUgbmFtZSBvZiBlYWNoIG9mIHRoZSBwYWNrYWdlcyB5b3Ugd2FudCB0byBpbnN0YWxsLiBTb21lIHBhY2thZ2VzIChsaWtlIG15IGBnYXJkZW5SYCBwYWNrYWdlKSBuZWVkcyB0byBiZSBpbnN0YWxsZWQgaW4gYSBzcGVjaWFsIHdheSB1c2luZyB0aGUgYGluc3RhbGxfZ2l0aHViKClgIGZ1bmN0aW9uIGluIHRoZSBgcmVtb3Rlc2AgbGlicmFyeSAtIHVuY29tbWVudCAoZGVsZXRlIHRoZSBoYXNodGFncyBmcm9tIHRoZSBmcm9udCkgdGhvc2UgdHdvIGxpbmVzIG9mIGNvZGUgdG8gaW5zdGFsbCB0aGUgbGlicmFyeS4gVGhlbiBlaXRoZXIgZGVsZXRlIHRob3NlIHR3byBsaW5lcyBvciBjb21tZW50IHRoZW0gYWdhaW4uIFlvdSBvbmx5IG5lZWQgdG8gKmluc3RhbGwqIHBhY2thZ2VzIG9uY2UsIGFsdGhvdWdoIHlvdSB3aWxsIG5lZWQgdG8gcmUtaW5zdGFsbCB0aGVtIGlmIHlvdSB1cGdyYWRlIHRvIGEgbmV3IHZlcnNpb24gb2YgUi4gWW91IG5lZWQgdG8gbG9hZCB0aGVtIHdpdGggdGhlIGBsaWJyYXJ5KClgIHN0YXRlbWVudHMgZWFjaCB0aW1lIHlvdSB1c2UgdGhlbS4gVGhlcmUgaXMgYSBnb29kIGFuYWxvZ3kgd2l0aCBsaWdodHM6IGluc3RhbGxpbmcgdGhlIHBhY2thZ2UgaXMgbGlrZSBwdXR0aW5nIHRoZSBsaWdodCBpbiB0aGUgc29ja2V0LCBsb2FkaW5nIHRoZSBwYWNrYWdlIGlzIGxpa2UgdHVybmluZyB0aGUgbGlnaHQgb24uCgpgYGB7ciBsaWJyYXJpZXN9CmxpYnJhcnkodGlkeXZlcnNlKSAgICAgICAgICMgZm9yIGdyYXBoaW5nIGFuZCBkYXRhIGNsZWFuaW5nCmxpYnJhcnkobHVicmlkYXRlKSAgICAgICAgICMgZm9yIHdvcmtpbmcgd2l0aCBkYXRlcwpsaWJyYXJ5KHBhbG1lcnBlbmd1aW5zKSAgICAjIGZvciBwYWxtZXIgcGVuZ3VpbiBkYXRhCiMgbGlicmFyeShyZW1vdGVzKSAgICAgICAgIyBmb3IgaW5zdGFsbGluZyBwYWNrYWdlIGZyb20gR2l0SHViCiMgcmVtb3Rlczo6aW5zdGFsbF9naXRodWIoImxsZW5kd2F5L2dhcmRlblIiKSAjIHJ1biBpZiBwYWNrYWdlIGlzIG5vdCBhbHJlYWR5IGluc3RhbGxlZApsaWJyYXJ5KGdhcmRlblIpICAgICAgICAgICAjIGZvciBMaXNhJ3MgZ2FyZGVuIGRhdGEKdGhlbWVfc2V0KHRoZW1lX21pbmltYWwoKSkgIyBteSBmYXZvcml0ZSBnZ3Bsb3QgdGhlbWUKYGBgCgpgYGB7ciBteV9saWJyYXJpZXMsIGluY2x1ZGU9RkFMU0V9CmxpYnJhcnkoZG93bmxvYWR0aGlzKSAjIGZvciBpbmNsdWRpbmcgZG93bmxvYWQgYnV0dG9ucyBmb3IgZmlsZXMKYGBgCgozLiBMb2FkIGRhdGEgdGhhdCB3aWxsIGJlIHVzZWQuIERhdGEgZnJvbSBwYWNrYWdlcyBjYW4gYmUgbG9hZGVkIHVzaW5nIHRoZSBgZGF0YSgpYCBmdW5jdGlvbi4gRGF0YSBvdXRzaWRlIG9mIGEgcGFja2FnZSBjYW4gYmUgbG9hZGVkIGluIGRpZmZlcmVudCB3YXlzIGRlcGVuZGluZyB3aGVyZSBpdCBpcyBhbmQgd2hhdCB0eXBlIG9mIGRhdGEgaXQgaXMuIExhdGVyIGluIHRoZSBjb3Vyc2UsIHdlIHdpbGwgbGVhcm4gZGlmZmVyZW50IGZ1bmN0aW9ucyB0aGF0IGNhbiBiZSB1c2VkIHRvIHJlYWQgaW4gZGF0YSBmcm9tIG90aGVyIHBsYWNlcy4KCmBgYHtyIGRhdGF9CiMgUGFsbWVyIFBlbmd1aW5zIGRhdGEgZnJvbSBwYWxtZXJwZW5ndWlucyBsaWJyYXJ5CmRhdGEoInBlbmd1aW5zIikKCiMgTGlzYSdzIGdhcmRlbiBkYXRhIGZyb20gZ2FyZGVuUiBsaWJyYXJ5CmRhdGEoImdhcmRlbl9oYXJ2ZXN0IikKYGBgCgpgYGB7ciBnYXJkZW4tZGF0YSwgaW5jbHVkZT1GQUxTRX0KIyBMaXNhJ3MgZ2FyZGVuIGRhdGEgb24gaGVyIGdvb2dsZSBkcml2ZSAtIG9sZCB3YXkgb2YgbG9hZGluZyBkYXRhIHByZSBnYXJkZW5SCiMgbGlicmFyeShnb29nbGVzaGVldHM0KSAgICAgIyBmb3IgcmVhZGluZyBpbiBkYXRhIGZyb20gZ29vZ2xlc2hlZXRzCiMgZ3M0X2RlYXV0aCgpICAgICAgICAgICAgICAgIyBza2lwcyBnb29nbGUgYXV0aG9yaXphdGlvbgojIAojIGdhcmRlbl9oYXJ2ZXN0IDwtIHJlYWRfc2hlZXQoImh0dHBzOi8vZG9jcy5nb29nbGUuY29tL3NwcmVhZHNoZWV0cy9kLzFEZWtTYXpDektxUFMyam5HaEt1ZTd0THhSVTNHVkwxb3hpLTRiRU01SVd3L2VkaXQ/dXNwPXNoYXJpbmciKSAlPiUgCiMgICBtdXRhdGUoZGF0ZSA9IHltZChkYXRlKSkKYGBgCgoKIyMgTW90aXZhdGlvbgoKQmVmb3JlIGp1bXBpbmcgaW50byB0ZWFjaGluZyB5b3Ugc29tZSBEYXRhIFNjaWVuY2Ugc2tpbGxzIGluIFIsIEkgd2FudCB0byBnaXZlIHlvdSBzb21lIG1vdGl2YXRpb24uIEkgcGlja2VkIHRocmVlIGdyYXBocyBJJ3ZlIHJlY2VudGx5IHNlZW4gb24gVHdpdHRlci4gVGhlc2UgYXJlIGFsbCByZXNwb25zZXMgdG8gYCNUaWR5VHVlc2RheWAgd2hpY2ggeW91J2xsIGJlIHBhcnRpY2lwYXRpbmcgaW4gdmVyeSBzb29uISBSZWFkIG1vcmUgYWJvdXQgaXQgW2hlcmVdKGh0dHBzOi8vdGhvbWFzbW9jay5uZXRsaWZ5LmFwcC9wb3N0L3RpZHl0dWVzZGF5LWEtd2Vla2x5LXNvY2lhbC1kYXRhLXByb2plY3QtaW4tci8pIGlmIHlvdSdyZSBjdXJpb3VzLiBUaGVyZSBhcmUgbWFueSBkZWZpbml0aW9ucyBvZiBEYXRhIFNjaWVuY2UgYnV0IEkgYnJvYWRseSBsaWtlIHRvIHRoaW5rIG9mIGl0IGFzIHVzaW5nIGRhdGEgdG8gdGVsbCBhIHN0b3J5LiBUaGVzZSB0aHJlZSBncmFwaHMgYXJlIGp1c3QgYSBzbWFsbCBzYW1wbGUgb2YgZG9pbmcganVzdCB0aGF0LgoKT25lIG9mIG15IGZhdm9yaXRlIERhdGEgVmlzdWFsaXplcnMgb24gVHdpdHRlcjoKCjxibG9ja3F1b3RlIGNsYXNzPSJ0d2l0dGVyLXR3ZWV0Ij48cCBsYW5nPSJlbiIgZGlyPSJsdHIiPkFuZCBoZXJlJiMzOTtzIHRoZSA8YSBocmVmPSJodHRwczovL3R3aXR0ZXIuY29tL2hhc2h0YWcvbWFraW5nb2Y/c3JjPWhhc2gmYW1wO3JlZl9zcmM9dHdzcmMlNUV0ZnciPiNtYWtpbmdvZjwvYT4gb2YgdGhpcyB3ZWVrJiMzOTtzIDxhIGhyZWY9Imh0dHBzOi8vdHdpdHRlci5jb20vaGFzaHRhZy9UaWR5VHVlc2RheT9zcmM9aGFzaCZhbXA7cmVmX3NyYz10d3NyYyU1RXRmdyI+I1RpZHlUdWVzZGF5PC9hPiBzdWJtaXNzaW9uPGEgaHJlZj0iaHR0cHM6Ly90d2l0dGVyLmNvbS9oYXNodGFnL2RhdGF2aXo/c3JjPWhhc2gmYW1wO3JlZl9zcmM9dHdzcmMlNUV0ZnciPiNkYXRhdml6PC9hPiA8YSBocmVmPSJodHRwczovL3R3aXR0ZXIuY29tL2hhc2h0YWcvcnN0YXRzP3NyYz1oYXNoJmFtcDtyZWZfc3JjPXR3c3JjJTVFdGZ3Ij4jcnN0YXRzPC9hPiA8YSBocmVmPSJodHRwczovL3QuY28velB2anM0S2RhSCI+aHR0cHM6Ly90LmNvL3pQdmpzNEtkYUg8L2E+IDxhIGhyZWY9Imh0dHBzOi8vdC5jby9pcVR1T0ZwUDRiIj5waWMudHdpdHRlci5jb20vaXFUdU9GcFA0YjwvYT48L3A+Jm1kYXNoOyBHZW9yZ2lvcyBLYXJhbWFuaXMgKEBnZW9rYXJhbWFuaXMpIDxhIGhyZWY9Imh0dHBzOi8vdHdpdHRlci5jb20vZ2Vva2FyYW1hbmlzL3N0YXR1cy8xMjUxNTUzNjA1Nzg3NzU0NTAzP3JlZl9zcmM9dHdzcmMlNUV0ZnciPkFwcmlsIDE4LCAyMDIwPC9hPjwvYmxvY2txdW90ZT4gPHNjcmlwdCBhc3luYyBzcmM9Imh0dHBzOi8vcGxhdGZvcm0udHdpdHRlci5jb20vd2lkZ2V0cy5qcyIgY2hhcnNldD0idXRmLTgiPjwvc2NyaXB0PgoKT25lIG9mIG15IGZvcm1lciBzdHVkZW50cyAoYW5kIHlvdXIgcHJlY2VwdG9yISk6Cgo8YmxvY2txdW90ZSBjbGFzcz0idHdpdHRlci10d2VldCI+PHAgbGFuZz0iZW4iIGRpcj0ibHRyIj5UaGlzIHdrJiMzOTtzIDxhIGhyZWY9Imh0dHBzOi8vdHdpdHRlci5jb20vUjREU2NvbW11bml0eT9yZWZfc3JjPXR3c3JjJTVFdGZ3Ij5AUjREU2NvbW11bml0eTwvYT4gPGEgaHJlZj0iaHR0cHM6Ly90d2l0dGVyLmNvbS9oYXNodGFnL1RpZHlUdWVzZGF5P3NyYz1oYXNoJmFtcDtyZWZfc3JjPXR3c3JjJTVFdGZ3Ij4jVGlkeVR1ZXNkYXk8L2E+OiBndWVzcyB3aGF0IGEgY2VudGVyZWQgZG90LXBsb3Qgb2YgYXN0cm9uYXV0cyBpbiBzcGFjZSBieSB5ZWFyIGFuZCBuYXRpb24gbG9va3MgYSBsb3QgbGlrZT88YnI+PGJyPkEgc3BhY2Ugc3RhdGlvbiBpbiBtaWQtb3JiaXQgKG9yIENsb3VkIENpdHkpISA8YSBocmVmPSJodHRwczovL3R3aXR0ZXIuY29tL2hhc2h0YWcvUlN0YXRzP3NyYz1oYXNoJmFtcDtyZWZfc3JjPXR3c3JjJTVFdGZ3Ij4jUlN0YXRzPC9hPiA8YSBocmVmPSJodHRwczovL3R3aXR0ZXIuY29tL2hhc2h0YWcvcjRkcz9zcmM9aGFzaCZhbXA7cmVmX3NyYz10d3NyYyU1RXRmdyI+I3I0ZHM8L2E+IDxhIGhyZWY9Imh0dHBzOi8vdHdpdHRlci5jb20vaGFzaHRhZy9EYXRhU2NpZW5jZT9zcmM9aGFzaCZhbXA7cmVmX3NyYz10d3NyYyU1RXRmdyI+I0RhdGFTY2llbmNlPC9hPiA8YSBocmVmPSJodHRwczovL3R3aXR0ZXIuY29tL2hhc2h0YWcvRGF0YVZpej9zcmM9aGFzaCZhbXA7cmVmX3NyYz10d3NyYyU1RXRmdyI+I0RhdGFWaXo8L2E+IDxhIGhyZWY9Imh0dHBzOi8vdHdpdHRlci5jb20vaGFzaHRhZy90aWR5dmVyc2U/c3JjPWhhc2gmYW1wO3JlZl9zcmM9dHdzcmMlNUV0ZnciPiN0aWR5dmVyc2U8L2E+IDxhIGhyZWY9Imh0dHBzOi8vdHdpdHRlci5jb20vaGFzaHRhZy9nZ3Bsb3QyP3NyYz1oYXNoJmFtcDtyZWZfc3JjPXR3c3JjJTVFdGZ3Ij4jZ2dwbG90MjwvYT4gPGEgaHJlZj0iaHR0cHM6Ly90LmNvL2hxVzdLTFdtc24iPnBpYy50d2l0dGVyLmNvbS9ocVc3S0xXbXNuPC9hPjwvcD4mbWRhc2g7IGxpbCBib2JieSB0YWJsZXMg8J+QsyAoQHJvYmVydF9iXykgPGEgaHJlZj0iaHR0cHM6Ly90d2l0dGVyLmNvbS9yb2JlcnRfYl8vc3RhdHVzLzEyODMzMDc0OTY4NzkzOTQ4MTY/cmVmX3NyYz10d3NyYyU1RXRmdyI+SnVseSAxNSwgMjAyMDwvYT48L2Jsb2NrcXVvdGU+IDxzY3JpcHQgYXN5bmMgc3JjPSJodHRwczovL3BsYXRmb3JtLnR3aXR0ZXIuY29tL3dpZGdldHMuanMiIGNoYXJzZXQ9InV0Zi04Ij48L3NjcmlwdD4KCkEgYCNUaWR5VHVlc2RheWAgbmV3Y29tZXI6Cgo8YmxvY2txdW90ZSBjbGFzcz0idHdpdHRlci10d2VldCI+PHAgbGFuZz0iZW4iIGRpcj0ibHRyIj5NeSBmaXJzdCA8YSBocmVmPSJodHRwczovL3R3aXR0ZXIuY29tL2hhc2h0YWcvVGlkeVR1ZXNkYXk/c3JjPWhhc2gmYW1wO3JlZl9zcmM9dHdzcmMlNUV0ZnciPiNUaWR5VHVlc2RheTwvYT4hIEkgZGVjaWRlZCB0byBLLkkuUy5TLiBhbmQgZm9jdXMgb24gYWVzdGhldGljIGZvciBteSBmaXJzdCB3ZWVrLiBUaGFuayB5b3UgPGEgaHJlZj0iaHR0cHM6Ly90d2l0dGVyLmNvbS9rbGx5Y3R0bj9yZWZfc3JjPXR3c3JjJTVFdGZ3Ij5Aa2xseWN0dG48L2E+IGZvciBwb2ludGluZyBtZSB0byB0aGUgZnV0dXJldmlzaW9ucyBwYWxldHRlcyEgPGJyPjxicj5HaXRIdWI6IDxhIGhyZWY9Imh0dHBzOi8vdC5jby9TNVlQMHBGbHZxIj5odHRwczovL3QuY28vUzVZUDBwRmx2cTwvYT48YnI+ZnV0dXJldmlzaW9uczogPGEgaHJlZj0iaHR0cHM6Ly90LmNvL2gwZGZVWUZPcWkiPmh0dHBzOi8vdC5jby9oMGRmVVlGT3FpPC9hPiA8YSBocmVmPSJodHRwczovL3QuY28vN2hxc3o3Y3dkYiI+cGljLnR3aXR0ZXIuY29tLzdocXN6N2N3ZGI8L2E+PC9wPiZtZGFzaDsgS2VsbHkgTW9ycm93IE1jQ2FydGh5IChAS2VsbHlNTV9uZXVybykgPGEgaHJlZj0iaHR0cHM6Ly90d2l0dGVyLmNvbS9LZWxseU1NX25ldXJvL3N0YXR1cy8xMjk2NTc3MzEyNzcxNzYwMTMwP3JlZl9zcmM9dHdzcmMlNUV0ZnciPkF1Z3VzdCAyMCwgMjAyMDwvYT48L2Jsb2NrcXVvdGU+IDxzY3JpcHQgYXN5bmMgc3JjPSJodHRwczovL3BsYXRmb3JtLnR3aXR0ZXIuY29tL3dpZGdldHMuanMiIGNoYXJzZXQ9InV0Zi04Ij48L3NjcmlwdD4KCiMjIExlYXJuaW5nIGdvYWxzCgpBZnRlciB0aGlzIHR1dG9yaWFsLCB5b3Ugc2hvdWxkIGJlIGFibGUgdG8gZG8gdGhlIGZvbGxvd2luZy4KCiogQ29uc3RydWN0IHRoZSAiRml2ZSBOYW1lZCBHcmFwaHMiIHVzaW5nIGBnZ3Bsb3QyYCBmdW5jdGlvbnMuICAKKiBBZGQgbGFiZWxzIHRvIGdyYXBocy4gIAoqIENoYW5nZSB0aGUgdGhlbWUgb2YgYSBncmFwaC4gIAoqIEludGVycHJldCBvciBleHBsYWluIHRoZSBncmFwaCB5b3UgY3JlYXRlZC4gIAoqIFVzZSB0aGUgc2l4IG1haW4gYGRwbHlyYCBmdW5jdGlvbnMgdG8gYmVnaW4gIndyYW5nbGluZyIgZGF0YS4gIAoqIFBpcGUgKGAlPiVgKSB0b2dldGhlciBhIHNlcXVlbmNlIG9mIGBkcGx5cmAgZnVuY3Rpb25zIHRvIGFuc3dlciBhIHF1ZXN0aW9uLiAgCiogQ29tYmluZSBgZHBseXJgIHZlcmJzIGFuZCBgZ2dwbG90KClgIGZ1bmN0aW9ucyB0byB3cmFuZ2xlIGFuZCBwbG90IGRhdGEuCgojIyBEYXRhCgpXZSB3aWxsIHVzZSB0d28gZGlmZmVyZW50IGRhdGFzZXRzIHRocm91Z2hvdXQgdGhpcyB0dXRvcmlhbC4gCgojIyMgUGFsbWVyIFBlbmd1aW5zCgpUaGUgW1BhbG1lciBQZW5ndWluc10oaHR0cHM6Ly9hbGxpc29uaG9yc3QuZ2l0aHViLmlvL3BhbG1lcnBlbmd1aW5zL2luZGV4Lmh0bWwjYWJvdXQtdGhlLWRhdGEpIGRhdGFzZXQgaXMgZnJvbSB0aGUgYHBhbG1lcnBlbmd1aW5zYCBsaWJyYXJ5LiBUaGUgZGF0YSB3ZSB3aWxsIHVzZSBpcyBjYWxsZWQgYHBlbmd1aW5zYC4gWW91IGNhbiByZWFkIGFib3V0IGl0IHdpdGhpbiBSIGJ5IHR5cGluZyBgP3Blbmd1aW5zYCBpbiB0aGUgY29uc29sZS4KCkxldCdzIGRvIHNvbWUgYmFzaWMgZXhwbG9yYXRpb24gb2YgdGhlIGRhdGEuIFRoZSBjb2RlIGJlbG93IHVzZXMgdGhlIGBkaW0oKWAgZnVuY3Rpb24gdG8gZmluZCB0aGUgZGltZW5zaW9ucyBvZiB0aGUgZGF0YXNldCAtIHRoZSBudW1iZXIgb2Ygcm93cyBhbmQgY29sdW1ucy4gCgpgYGB7ciBwZW5ndWluLWRpbX0KZGltKHBlbmd1aW5zKQpgYGAKCkFuZCB3ZSB1c2UgdGhlIGBoZWFkKClgIGZ1bmN0aW9uIHRvIHZpZXcgdGhlIGZpcnN0IDYgcm93cyBvZiB0aGUgZGF0YS4gCgpgYGB7ciBwZW5ndWluLWhlYWR9CmhlYWQocGVuZ3VpbnMpCmBgYAoKCiMjIyBMaXNhJ3MgR2FyZGVuIERhdGEKCgpUaGUgYGdhcmRlbl9oYXJ2ZXN0YCBkYXRhIGNvbnRhaW5zIGRhdGEgdGhhdCBJIGhhdmUgY29sbGVjdGVkICBmcm9tIG15IHBlcnNvbmFsIGdhcmRlbiBpbiB0aGUgc3VtbWVyIG9mIDIwMjAuIFlvdSBjYW4gdmlldyB0aGUgb3JpZ2luYWwgZ29vZ2xlIHNoZWV0IFtoZXJlXShodHRwczovL2RvY3MuZ29vZ2xlLmNvbS9zcHJlYWRzaGVldHMvZC8xZFBWSHdaZ1I5QnhwaWdiSExuQTBVOTlUdFZISFF0VXpOQjlVUjB3dmI3by9lZGl0P3VzcD1zaGFyaW5nKS4gRWFjaCByb3cgaW4gdGhlIGRhdGEgaXMgYSAiaGFydmVzdCIgZm9yIGEgdmFyaWV0eSBvZiBhIHZlZ2V0YWJsZS4gU28sIHZlZ2V0YWJsZXMgbWlnaHQgaGF2ZSBtdWx0aXBsZSByb3dzIG9uIGEgZGF5LCBlc3BlY2lhbGx5IGlmIHRoZXkgYXJlIHRoaW5ncyBJIGVhdCB0d2ljZSBhIGRheSAobGV0dHVjZSkgb3IgdGhlcmUgYXJlIG1hbnkgZGlmZmVyZW50IHZhcmlldGllcyBvZiB0aGUgdmVnZXRhYmxlICh0b21hdG9lcykuIAoKSSBmb25kbHkgcmVmZXIgdG8gbXkgZ2FyZGVuIGFzIHRoZSAiSnVuZ2xlIEdhcmRlbiIgYmVjYXVzZSBieSB0aGUgZW5kIG9mIHRoZSBzdW1tZXIgYWxsIHRoZSBwbGFudHMgYXJlIGNyZWVwaW5nIG91dCBvZiB0aGVpciBiZWRzIGFuZCBpdCBjYW4gYmUgcXVpdGUgdGhlIGFkdmVudHVyZSB3YWxraW5nIHRocm91Z2ggaXQuIFRha2UgYSBsb29rIGF0IHRoZSB2aWRlbyBiZWxvdyBmb3IgYW4gaW4tZGVwdGggdG91ciBvZiB0aGUgZ2FyZGVuIGFuZCBkZXRhaWxzIGFyb3VuZCBob3cgSSBjb2xsZWN0IHRoZSBkYXRhLiAKCjxpZnJhbWUgd2lkdGg9IjU2MCIgaGVpZ2h0PSIzMTUiIHNyYz0iaHR0cHM6Ly93d3cueW91dHViZS5jb20vZW1iZWQvaUdNZ0xGSWlTeG8iIGZyYW1lYm9yZGVyPSIwIiBhbGxvdz0iYWNjZWxlcm9tZXRlcjsgYXV0b3BsYXk7IGVuY3J5cHRlZC1tZWRpYTsgZ3lyb3Njb3BlOyBwaWN0dXJlLWluLXBpY3R1cmUiIGFsbG93ZnVsbHNjcmVlbj48L2lmcmFtZT4KCltWb2ljZXRocmVhZDogSnVuZ2xlIEdhcmRlbiB0b3VyXShodHRwczovL3ZvaWNldGhyZWFkLmNvbS9zaGFyZS8xNTA4MjY3NC8pCgpMZXQncyBhbHNvIGdldCBhbiBvdmVydmlldyBvZiB0aGlzIGRhdGFzZXQuCgpVc2UgdGhlIGBkaW0oKWAgZnVuY3Rpb24gdG8gZmluZCB0aGUgbnVtYmVyIG9mIGNhc2VzIGFuZCB2YXJpYWJsZXMgaW4gdGhlIGRhdGFzZXQuCgpgYGB7ciBnYXJkZW4tZGltfQoKYGBgCgpVc2UgdGhlIGBnbGltcHNlKClgIGZ1bmN0aW9uIHRvIHNob3cgdGhlIGZpcnN0IGZldyBjYXNlcyBvZiBlYWNoIG9mIHRoZSB2YXJpYWJsZXMgYW5kIHNlZSB0aGUgdHlwZSBvZiB2YXJpYWJsZS4KCmBgYHtyIGdhcmRlbi1nbGltcHNlfQoKYGBgCgoKIyMgQ3JlYXRpbmcgZ3JhcGhzIHdpdGggYGdncGxvdCgpYAoKTm93LCBsZXQncyBnZXQgcmVhZHkgdG8gcGxvdCBzb21lIGRhdGEhIFRoZSBjb25jZXB0IG1hcCBiZWxvdyBwcm92aWRlcyBhbiBvdmVydmlldyBvZiB0aGUgZnVuY3Rpb25zIHlvdSB3aWxsIGJlIGxlYXJuaW5nLCBob3cgdGhleSByZWxhdGUgdG8gb25lIGFub3RoZXIsIGFuZCB3aGF0IHRoZXkgZG8uCgo8Y2VudGVyPgohW10oLi4vLi4vaW1hZ2VzL2dncGxvdF9jb25jZXB0X21hcC5qcGcpe3dpZHRoPTYwMHB4fQo8L2NlbnRlcj4KCkZpcnN0LCB3YXRjaCB0aGUgdmlkZW8gYmVsb3cgdGhhdCBpbnRyb2R1Y2VzIHRoZSBgZ2dwbG90KClgIHN5bnRheC4gWW91IGNhbiBkb3dubG9hZCB0aGUgc2xpZGVzLiBUaGV5IHdpbGwgb3BlbiBpbiBhIHdlYiBicm93c2VyLCBhbmQgeW91IGNhbiBwcmVzcyB0aGUgbGV0dGVyICJwIiB0byBnbyB0byBwcmVzZW50YXRpb24gbW9kZSBhbmQgc2VlIG15IG5vdGVzIHRoYXQgZ28gYWxvbmcgd2l0aCB0aGVtLgoKPGlmcmFtZSB3aWR0aD0iNTYwIiBoZWlnaHQ9IjMxNSIgc3JjPSJodHRwczovL3d3dy55b3V0dWJlLmNvbS9lbWJlZC8wT3RZMzhMVnktbyIgZnJhbWVib3JkZXI9IjAiIGFsbG93PSJhY2NlbGVyb21ldGVyOyBhdXRvcGxheTsgZW5jcnlwdGVkLW1lZGlhOyBneXJvc2NvcGU7IHBpY3R1cmUtaW4tcGljdHVyZSIgYWxsb3dmdWxsc2NyZWVuPjwvaWZyYW1lPgoKW1ZvaWNldGhyZWFkOiBJbnRybyB0byBgZ2dwbG90KClgXShodHRwczovL3ZvaWNldGhyZWFkLmNvbS9zaGFyZS8xNTA1Njg4MS8pCgpgYGB7ciwgZWNobz1GQUxTRX0KZG93bmxvYWRfZmlsZSgKICBwYXRoID0gIi4uLzAxX2dncGxvdC8wMV9nZ3Bsb3QuaHRtbCIsCiAgYnV0dG9uX2xhYmVsID0gIkRvd25sb2FkIGdncGxvdCgpIHNsaWRlcyIsCiAgYnV0dG9uX3R5cGUgPSAiaW5mbyIsCiAgaGFzX2ljb24gPSBUUlVFLAogIGljb24gPSAiZmEgZmEtc2F2ZSIsCiAgc2VsZl9jb250YWluZWQgPSBGQUxTRQopCmBgYAoKTmV4dCwgd2F0Y2ggdGhlIHZpZGVvIGJlbG93IHRoYXQgd2Fsa3MgdGhyb3VnaCBzb21lIGV4YW1wbGVzIGluIFIgU3R1ZGlvLiBZb3UgY2FuIHByYWN0aWNlIGFsb25nIHdpdGggbWUgYnkgZG93bmxvYWRpbmcgdGhlIFIgTWFya2Rvd24gZmlsZSBhbmQgd29ya2luZyB0aHJvdWdoIHRoZSBwcm9ibGVtcy4gSWYgeW91IGRvIHRoYXQsIHlvdSB3aWxsIGxpa2VseSBnZXQgc29tZXdoYXQgZGlmZmVyZW50IHJlc3VsdHMgdGhhbiB5b3Ugc2VlIGluIHRoZSB2aWRlbyB3aGVuIHVzaW5nIHRoZSBgZ2FyZGVuX2hhcnZlc3RgIGRhdGEgYmVjYXVzZSBJIG1hZGUgdGhlIHZpZGVvcyB3aGVuIEkgd2FzIHN0aWxsIGluIHRoZSBtaWRzdCBvZiBjb2xsZWN0aW5nIGRhdGEgOikKCjxpZnJhbWUgd2lkdGg9IjU2MCIgaGVpZ2h0PSIzMTUiIHNyYz0iaHR0cHM6Ly93d3cueW91dHViZS5jb20vZW1iZWQvMVNZelZNSDYyeXciIGZyYW1lYm9yZGVyPSIwIiBhbGxvdz0iYWNjZWxlcm9tZXRlcjsgYXV0b3BsYXk7IGVuY3J5cHRlZC1tZWRpYTsgZ3lyb3Njb3BlOyBwaWN0dXJlLWluLXBpY3R1cmUiIGFsbG93ZnVsbHNjcmVlbj48L2lmcmFtZT4KCltWb2ljZXRocmVhZDogYGdncGxvdCgpYCBkZW1vXShodHRwczovL3ZvaWNldGhyZWFkLmNvbS9zaGFyZS8xNTA1Njk0Ny8pCgpgYGB7ciwgZWNobz1GQUxTRX0KZG93bmxvYWRfZmlsZSgKICBwYXRoID0gIi4uLzAxX2dncGxvdC8wMV9nZ3Bsb3RfZGVtb19ub19jb2RlLlJtZCIsCiAgYnV0dG9uX2xhYmVsID0gIkRvd25sb2FkIGdncGxvdCgpIGRlbW8gZmlsZSAod2l0aG91dCBjb2RlKSIsCiAgYnV0dG9uX3R5cGUgPSAid2FybmluZyIsCiAgaGFzX2ljb24gPSBUUlVFLAogIGljb24gPSAiZmEgZmEtc2F2ZSIsCiAgc2VsZl9jb250YWluZWQgPSBGQUxTRQopCmBgYAoKYGBge3IsIGVjaG89RkFMU0V9CmRvd25sb2FkX2ZpbGUoCiAgcGF0aCA9ICIuLi8wMV9nZ3Bsb3QvMDFfZ2dwbG90X2RlbW8uUm1kIiwKICBidXR0b25fbGFiZWwgPSAiRG93bmxvYWQgZ2dwbG90KCkgZGVtbyBmaWxlICh3aXRoIGNvZGUpIiwKICBidXR0b25fdHlwZSA9ICJpbmZvIiwKICBoYXNfaWNvbiA9IFRSVUUsCiAgaWNvbiA9ICJmYSBmYS1zYXZlIiwKICBzZWxmX2NvbnRhaW5lZCA9IEZBTFNFCikKYGBgCgpMYXN0bHksIHdhdGNoIHRoaXMgc2hvcnQgdmlkZW8gYWJvdXQgY29tbW9uIG1pc3Rha2VzLiBIb3BlZnVsbHkgeW91IHdvbid0IG1ha2UgdGhlbSwgYnV0IGFkbWl0dGVkbHkgSSBzb21ldGltZXMgc3RpbGwgZG8uCgo8aWZyYW1lIHdpZHRoPSI1NjAiIGhlaWdodD0iMzE1IiBzcmM9Imh0dHBzOi8vd3d3LnlvdXR1YmUuY29tL2VtYmVkL2o3Ri1JZEpBT0pzIiBmcmFtZWJvcmRlcj0iMCIgYWxsb3c9ImFjY2VsZXJvbWV0ZXI7IGF1dG9wbGF5OyBlbmNyeXB0ZWQtbWVkaWE7IGd5cm9zY29wZTsgcGljdHVyZS1pbi1waWN0dXJlIiBhbGxvd2Z1bGxzY3JlZW4+PC9pZnJhbWU+CgpbVm9pY2V0aHJlYWQ6IGBnZ3Bsb3QoKWAgbWlzdGFrZXNdKGh0dHBzOi8vdm9pY2V0aHJlYWQuY29tL3NoYXJlLzE1MDU3MTc1LykKCiMjIE1vcmUgd2l0aCBgZ2dwbG90KClgIQoKSSBjb3VsZG4ndCByZXNpc3QgZ2l2aW5nIGEgZmV3IG1vcmUgdGlwcyBhbmQgdHJpY2tzIGZvciBjcmVhdGluZyBiZWF1dGlmdWwgcGxvdHMgdXNpbmcgYGdncGxvdCgpYCEKClZpZXcgdGhlIHZpZGVvIGJlbG93OgoKPGlmcmFtZSB3aWR0aD0iNTYwIiBoZWlnaHQ9IjMxNSIgc3JjPSJodHRwczovL3d3dy55b3V0dWJlLmNvbS9lbWJlZC9ia0FKM0ZBQUdxQSIgZnJhbWVib3JkZXI9IjAiIGFsbG93PSJhY2NlbGVyb21ldGVyOyBhdXRvcGxheTsgZW5jcnlwdGVkLW1lZGlhOyBneXJvc2NvcGU7IHBpY3R1cmUtaW4tcGljdHVyZSIgYWxsb3dmdWxsc2NyZWVuPjwvaWZyYW1lPgoKW1ZvaWNldGhyZWFkOiBNb3JlIHdpdGggYGdncGxvdCgpYF0oaHR0cHM6Ly92b2ljZXRocmVhZC5jb20vc2hhcmUvMTUyMTM3MjQvKQoKQW5kIGZvbGxvdyBhbG9uZyB3aXRoIHRoZSBjb2RlIQoKYGBge3IsIGVjaG89RkFMU0V9CmRvd25sb2FkX2ZpbGUoCiAgcGF0aCA9ICIuLi8wMV9nZ3Bsb3QvMDFfZ2dwbG90X21vcmVfZGVtby5SbWQiLAogIGJ1dHRvbl9sYWJlbCA9ICJEb3dubG9hZCBnZ3Bsb3QoKSBtb3JlIGZpbGUiLAogIGJ1dHRvbl90eXBlID0gImluZm8iLAogIGhhc19pY29uID0gVFJVRSwKICBpY29uID0gImZhIGZhLXNhdmUiLAogIHNlbGZfY29udGFpbmVkID0gRkFMU0UKKQpgYGAKCiMjIyBSZXNvdXJjZXMKCiogW1I0RFNdKGh0dHBzOi8vcjRkcy5oYWQuY28ubnovZGF0YS12aXN1YWxpc2F0aW9uLmh0bWwpIGNoYXB0ZXIgMyAgCiogW2dncGxvdDIgY2hlYXRzaGVldF0oaHR0cHM6Ly9yc3R1ZGlvLmNvbS9yZXNvdXJjZXMvY2hlYXRzaGVldHMvKSAoc2VhcmNoIGZvciBEYXRhIFZpc3VhbGl6YXRpb24pICAKKiBbZ2dwbG90MiBkb2N1bWVudGF0aW9uXShodHRwczovL2dncGxvdDIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2UvaW5kZXguaHRtbCkKKiBbUiBDb2xvciBuYW1lc10oaHR0cHM6Ly93d3cuci1ncmFwaC1nYWxsZXJ5LmNvbS80Mi1jb2xvcnMtbmFtZXMuaHRtbCkgCgoKIyMjIFlvdXIgdHVybiEKCk5vdyB5b3UgaGF2ZSB0aGUgdG9vbHMgeW91IG5lZWQgdG8gYmVnaW4gY3JlYXRpbmcgeW91ciBvd24gcGxvdHMuIEFzIHlvdSB3b3JrIHRocm91Z2ggdGhlc2UgZXhlcmNpc2VzLCBpdCB3aWxsIGJlIGhlbHBmdWwgdG8gaGF2ZSB0aGUgKkRhdGEgVmlzdWFsaXphdGlvbiB3aXRoIGBnZ3Bsb3QyYCogY2hlYXRzaGVldCBvcGVuLiBGaW5kIHRoZSBjaGVhdHNoZWV0IFtoZXJlXShodHRwczovL3JzdHVkaW8uY29tL3Jlc291cmNlcy9jaGVhdHNoZWV0cy8pIG9yLCBmcm9tIHdpdGhpbiBSIFN0dWRpbywgZ28gdG8gSGVscCAtLT4gQ2hlYXRzaGVldHMgYW5kIGNsaWNrIG9uICpEYXRhIFZpc3VhbGl6YXRpb24gd2l0aCBgZ2dwbG90MmAqLgoKIyMjIyBFeGVyY2lzZSAxYTogU2NhdHRlcnBsb3RzCgpVc2UgdGhlIGBwZW5ndWluc2AgZGF0YSB0byBjcmVhdGUgYSBzY2F0dGVycGxvdCBvZiBgYmlsbF9sZW5ndGhfbW1gICh4LWF4aXMpIHZzLiBgYmlsbF9kZXB0aF9tbWAgKHktYXhpcykuIEkgaGF2ZSBzdGFydGVkIHRoZSBjb2RlIGZvciB5b3UuIEhvdyB3b3VsZCBkbyBkZXNjcmliZSB0aGUgcmVsYXRpb25zaGlwPwoKYGBge3IgcGVuZ3Vpbi1zY2F0dGVyLCBldmFsPUZBTFNFfQpwZW5ndWlucyAlPiUgCiAgZ2dwbG90KCAgICh4ID0gICAgLCAKICAgICAgICAgICAgIHkgPSAgICApKSArCiAgZ2VvbV8oKQpgYGAKCgojIyMjIEV4ZXJjaXNlIDFiOiBTY2F0dGVycGxvdHMKCk5vdyB1c2UgdGhlIGNvZGUgeW91IHdyb3RlIGluIHRoZSBwcmV2aW91cyBleGVyY2lzZSBidXQgY29sb3IgdGhlIHBvaW50cyBieSBgc3BlY2llc2AuIEhvdyBkb2VzIHRoaXMgY2hhbmdlIGhvdyB5b3UgZGVzY3JpYmVkIHRoZSByZWxhdGlvbnNoaXAgYmVmb3JlPwoKYGBge3IgcGVuZ3Vpbi1zY2F0dGVyMn0KCmBgYAoKIyMjIyBDSEFMTEVOR0U6IFNjYXR0ZXJwbG90cyAKCk5vdyB1c2UgdGhlIGNvZGUgeW91IHdyb3RlIGluIHRoZSBwcmV2aW91cyBleGVyY2lzZSBidXQgbWFrZSB0aGUgcG9pbnRzIHNtYWxsZXIgYW5kIG1vcmUgdHJhbnNwYXJlbnQuCgpgYGB7ciBwZW5ndWluLXNjYXR0ZXIzfQoKYGBgCgoKIyMjIyBFeGVyY2lzZSAyYTogSGlzdG9ncmFtcwoKQ3JlYXRlIGEgaGlzdG9ncmFtIG9mIHRoZSBgZmxpcHBlcl9sZW5ndGhfbW1gLgoKYGBge3IgcGVuZ3Vpbi1oaXN0fQoKYGBgCgoKIyMjIyBFeGVyY2lzZSAyYjogSGlzdG9ncmFtcwoKQWRkIGEgYGZhY2V0YCB0byB0aGUgcHJldmlvdXMgaGlzdG9ncmFtIHNvIHRoZXJlIGlzIGEgZGlmZmVyZW50IGhpc3RvZ3JhbSBmb3IgZWFjaCBgc3BlY2llc2AuIE1ha2UgaXQgc28gdGhlcmUgaXMgb25lIGNvbHVtbiBvZiBwbG90cy4gSG93IHdvdWxkIHlvdSBjb21wYXJlIHRoZSBkaXN0cmlidXRpb25zPwoKYGBge3IgcGVuZ3Vpbi1oaXN0LWZhY2V0fQoKYGBgCgojIyMjIEV4ZXJjaXNlIDNhOiBCYXJwbG90cyAKCkNyZWF0ZSBhIGJhcnBsb3QgdGhhdCBzaG93cyB0aGUgbnVtYmVyIG9mIHBlbmd1aW5zIGZvciBlYWNoIHllYXIuIEZpbGwgaW4gdGhlIGJhcnMgd2l0aCB0aGUgY29sb3IgKmxpZ2h0Ymx1ZSouCgpgYGB7ciBwZW5ndWluLWJhcn0KCmBgYAoKIyMjIyBDSEFMTEVOR0U6IEJhcnBsb3RzIAoKVGhlIGNvZGUgYmVsb3cgY3JlYXRlcyBhIG5ldyBkYXRhc2V0IGNhbGxlZCBgdG9tYXRvZXNgLiBVc2UgdGhlIGB0b21hdG9lc2AgZGF0YXNldCB0byBjcmVhdGUgYSBiYXJwbG90IHRoYXQgc2hvd3MgdGhlIG51bWJlciBvZiBkYXlzIHRoYXQgZWFjaCB0b21hdG8gdmFyaWV0eSBoYXMgYmVlbiBoYXJ2ZXN0ZWQuIE1ha2UgdGhlIGJhcnMgaG9yaXpvbnRhbCwgZmlsbCB0aGVtIGluIHdpdGggdGhlIGNvbG9yICp0b21hdG80KiAsIG9yZGVyIHRoZW0gZnJvbSBtb3N0IHRvIGxlYXN0IChoaW50OiB1c2UgYGZjdF9pbmZyZXEoKWAgYW5kIGBmY3RfcmV2KClgKS4gQWxzbyBnaXZlIHRoZSBwbG90IG5pY2UgbGFiZWxzLgoKYGBge3IgdG9tYXRvZXMtYmFyfQp0b21hdG9lcyA8LSBnYXJkZW5faGFydmVzdCAlPiUgCiAgZmlsdGVyKHZlZ2V0YWJsZSA9PSAidG9tYXRvZXMiKSAKYGBgCgojIyMjIEV4ZXJjaXNlIDQ6IEJveHBsb3RzCgpVc2UgYm94cGxvdHMgdG8gY29tcGFyZSB0aGUgYGZsaXBwZXJfbGVuZ3RoX21tYCBieSBgc3BlY2llc2AuIE1ha2UgdGhlIGJveHBsb3RzIGhvcml6b250YWwuIEhvdyBkb2VzIHRoaXMgZ3JhcGggY29tcGFyZSB0byB0aGUgZmFjZXRlZCBoaXN0b2dyYW0geW91IG1hZGUgYWJvdmU/IFdoYXQgYXJlIHRoZSBzdHJlbmd0aHMgYW5kIHdlYWtuZXNzZXMgb2YgZWFjaCB0eXBlIG9mIGdyYXBoLgoKYGBge3IgcGVuZ3Vpbi1ib3gsIGV4ZXJjaXNlID0gVFJVRX0KCmBgYAoKIyMjIyBFeGVyY2lzZSA1YTogTGluZSBncmFwaHMKClRoZSBjb2RlIGJlbG93IGNyZWF0ZXMgYSBkYXRhc2V0IChgdG9tYXRvZXNfd3RfZGF0ZWApIHRoYXQgaGFzIHRoZSB3ZWlnaHQgaW4gZ3JhbXMgb2YgdG9tYXRvZXMgKGBkYWlseV93dF9nYCkgZm9yIGVhY2ggZGF0ZS4gVXNlIHRoYXQgdG8gY3JlYXRlIGEgbGluZWdyYXBoIG9mIHRoZSB3ZWlnaHQgb2YgdG9tYXRvZXMgaGFydmVzdGVkIGVhY2ggZGF5LiAKCmBgYHtyIHRvbWF0b2VzLWxpbmV9CnRvbWF0b2VzX3d0X2RhdGUgPC0gZ2FyZGVuX2hhcnZlc3QgJT4lIAogIGZpbHRlcih2ZWdldGFibGUgPT0gInRvbWF0b2VzIikgJT4lIAogIGdyb3VwX2J5KGRhdGUpICU+JSAKICBzdW1tYXJpemUoZGFpbHlfd3RfZyA9IHN1bSh3ZWlnaHQpKQpgYGAKCiMjIyMgRXhlcmNpc2UgNWI6IExpbmUgZ3JhcGhzCgpUaGUgY29kZSBiZWxvdyBjcmVhdGVzIGEgZGF0YXNldCAoYHRvbWF0b192YXJpZXR5X2RhaWx5YCkgdGhhdCBoYXMgdGhlIHdlaWdodCBpbiBncmFtcyBvZiBlYWNoIHZhcmlldHkgb2YgdG9tYXRvIChgZGFpbHlfd3RfZ2ApIGZvciBlYWNoIGRhdGUuIFVzZSB0aGF0IHRvIGNyZWF0ZSBhIGxpbmVncmFwaCBvZiB0aGUgd2VpZ2h0IG9mIHRvbWF0b2VzIGhhcnZlc3RlZCBlYWNoIGRheSwgd2hlcmUgdGhlcmUgaXMgYSBzZXBhcmF0ZSBsaW5lIGZvciBlYWNoIHZhcmlldHksIGluIGEgZGlmZmVyZW50IGNvbG9yLiBXaGF0IGFyZSBzb21lIHdheXMgeW91IG1pZ2h0IGltcHJvdmUgdGhpcyBncmFwaD8KCmBgYHtyIHRvbWF0by12YXJpZXR5LWxpbmV9CnRvbWF0b192YXJpZXR5X2RhaWx5IDwtIGdhcmRlbl9oYXJ2ZXN0ICU+JSAKICBmaWx0ZXIodmVnZXRhYmxlID09ICJ0b21hdG9lcyIpICU+JSAKICBncm91cF9ieShkYXRlLCB2YXJpZXR5KSAlPiUgCiAgc3VtbWFyaXplKGRhaWx5X3d0X2cgPSBzdW0od2VpZ2h0KSkKYGBgCgoKIyMgV3JhbmdsaW5nIGRhdGEgd2l0aCBgZHBseXJgIGZ1bmN0aW9ucwoKTmV4dCwgeW91IHdpbGwgbGVhcm4gaG93IHRvIHdyYW5nbGUgYW5kIG1hbmlwdWxhdGUgZGF0YSB1c2luZyBzaXggYGRwbHlyYCBmdW5jdGlvbnMuIFRoZXJlIGFyZSBtYW55IG90aGVyIGZ1bmN0aW9ucyB3ZSBjYW4gdXNlIChhbmQgd2Ugd2lsbCEpIGJ1dCB0aGVzZSBzaXggd2lsbCBnZXQgdXMgcHJldHR5IGZhciwgZXNwZWNpYWxseSB3aGVuIGNvbWJpbmVkLiBUaGUgY29uY2VwdCBtYXAgYmVsb3cgc2hvd3MgdGhlIHNpeCBmdW5jdGlvbnMgSSB3aWxsIGludHJvZHVjZSBhbmQgd2hhdCB0aGV5IGFyZSB1c2VkIGZvci4gCgo8Y2VudGVyPgoKIVtdKC4uLy4uL2ltYWdlcy9kcGx5cl9jb25jZXB0X21hcC5qcGcpe3dpZHRoPTYwMHB4fQo8L2NlbnRlcj4KCkZpcnN0LCB3YXRjaCB0aGUgdmlkZW8gYmVsb3cgdGhhdCBpbnRyb2R1Y2VzIHRoZSBgZHBseXJgIGZ1bmN0aW9ucy4gQWdhaW4sIHlvdSBjYW4gZG93bmxvYWQgdGhlIHNsaWRlcy4gVGhleSB3aWxsIG9wZW4gaW4gYSB3ZWIgYnJvd3NlciwgYW5kIHlvdSBjYW4gcHJlc3MgdGhlIGxldHRlciAicCIgdG8gZ28gdG8gcHJlc2VudGF0aW9uIG1vZGUgYW5kIHNlZSBteSBub3RlcyB0aGF0IGdvIGFsb25nIHdpdGggdGhlbS4KCjxpZnJhbWUgd2lkdGg9IjU2MCIgaGVpZ2h0PSIzMTUiIHNyYz0iaHR0cHM6Ly93d3cueW91dHViZS5jb20vZW1iZWQvZHFQMERlakk3bHciIGZyYW1lYm9yZGVyPSIwIiBhbGxvdz0iYWNjZWxlcm9tZXRlcjsgYXV0b3BsYXk7IGVuY3J5cHRlZC1tZWRpYTsgZ3lyb3Njb3BlOyBwaWN0dXJlLWluLXBpY3R1cmUiIGFsbG93ZnVsbHNjcmVlbj48L2lmcmFtZT4KCltWb2ljZXRocmVhZDogSW50cm8gdG8gYGRwbHlyYF0oaHR0cHM6Ly92b2ljZXRocmVhZC5jb20vc2hhcmUvMTUwNjkyNzUvKQoKYGBge3IsIGVjaG89RkFMU0V9CmRvd25sb2FkX2ZpbGUoCiAgcGF0aCA9ICIuLi8wMl9kcGx5ci8wMl9kcGx5cl9pbnRyby5odG1sIiwKICBidXR0b25fbGFiZWwgPSAiRG93bmxvYWQgZHBseXIgc2xpZGVzIiwKICBidXR0b25fdHlwZSA9ICJpbmZvIiwKICBoYXNfaWNvbiA9IFRSVUUsCiAgaWNvbiA9ICJmYSBmYS1zYXZlIiwKICBzZWxmX2NvbnRhaW5lZCA9IEZBTFNFCikKYGBgCgoKVG8gcmVjYXAsIHRoZSBzaXggbWFpbiBgZHBseXJgIHZlcmJzIGFyZSBzdW1tYXJpemVkIGJlbG93LgoKIVsqSW1hZ2VzIGZyb20gUiBTdHVkaW8gQ2hlYXRzaGVldHM6IGh0dHBzOi8vcnN0dWRpby5jb20vcmVzb3VyY2VzL2NoZWF0c2hlZXRzLypdKC4uLy4uL2ltYWdlcy9kcGx5cl92ZXJicy5wbmcpe3dpZHRoPTYwMHB4fQoKIyMjIFRoZSBtYWluIGBkcGx5cmAgZnVuY3Rpb25zIC4uLiBpbGx1c3RyYXRlZAoKSGVyZSwgSSBpbGx1c3RyYXRlIChJIHdpbGwgbm90IHByZXRlbmQgdG8gYmUgYW4gYXJ0aXN0KSBzb21lIG9mIHRoZSBgZHBseXJgIGZ1bmN0aW9ucyB0byBoaWdobGlnaHQgdGhlaXIgbWFpbiB1c2VzIC4uLiBhbmQgaG9wZWZ1bGx5IG1ha2UgeW91IHNtaWxlLiBJbiB0aGUgbWFkZSB1cCBkYXRhc2V0LCBjYWxsZWQgYGRhdGFgIChJIGtub3csIGl0J3MgYSB0ZXJyaWJsZSBuYW1lKSwgdGhlcmUgYXJlIHRocmVlIHZhcmlhYmxlczogYHBldGAgaXMgdGhlIHR5cGUgb2YgcGV0IHRoZSBzdHVkZW50IG93bnMgLSBhIGNhdCwgYSBkb2csIG9yIGEgZmlzaCAodGhleSBhbGwgb3duIGEgcGV0IGFuZCBvbmx5IG9uZSBpbiB0aGlzIGRhdGFzZXQpOyBgbl9jbGFzc2VzYCBpcyB0aGUgbnVtYmVyIG9mIGNsYXNzZXMgdGhlIHN0dWRlbnQgaXMgdGFraW5nIGluIHRoaXMgcXVhcnRlcjsgYW5kIGBob3Vyc19od2AgaXMgdGhlIG51bWJlciBvZiBob3VycyB0aGUgc3R1ZGVudCBzcGVuZHMgZG9pbmcgaG9tZXdvcmsgaW4gYSB3ZWVrLiBEZXNjcmlwdGlvbnMgYXJlIGZvdW5kIGJlbG93IGVhY2ggaWxsdXN0cmF0aW9uLgoKPGNlbnRlcj4KCiFbVGhlIHJhdyBkYXRhXShkcGx5cl9hcnQvZGF0YS5wbmcpe3dpZHRoPTUwJX0KCiFbV2UgY2hvb3NlIHZhcmlhYmxlcyB3aXRoIGBzZWxlY3QoKWBdKGRwbHlyX2FydC9zZWxlY3QucG5nKXt3aWR0aD01MCV9CgohW1dlIGFkZCB2YXJpYWJsZXMgKGtlZXBpbmcgdGhlIHNhbWUgbnVtYmVyIG9mIHJvd3MpIHdpdGggYG11dGF0ZSgpYC4gSW5zaWRlIHRoZSBgbXV0YXRlKClgIGZ1bmN0aW9uIHdlIG5lZWQgdG8gdGVsbCBpdCB0aGUgZGV0YWlscyBvZiBob3cgdG8gY29tcHV0ZSB0aGUgbmV3IHZhcmlhYmxlLl0oZHBseXJfYXJ0L211dGF0ZS5wbmcpe3dpZHRoPTUwJX0KCiFbV2UgY2hvb3NlIHJvd3Mgd2l0aCBgZmlsdGVyKClgXShkcGx5cl9hcnQvZmlsdGVyLnBuZyl7d2lkdGg9NTAlfQoKIVtXZSBvcmRlciByb3dzIHdpdGggYGFycmFuZ2UoKWBdKGRwbHlyX2FydC9hcnJhbmdlLnBuZyl7d2lkdGg9NTAlfQoKIVtXZSBncm91cCBkYXRhIHdpdGggYGdyb3VwX2J5KClgLCB3aGljaCBjaGFuZ2VzIHRoZSBkYXRhICJpbnRlcm5hbGx5IiBidXQgd29uJ3QgbWFrZSBpdCBsb29rIGRpZmZlcmVudC4gSWYgeW91IGZpbmQgeW91cnNlbGYgc2F5aW5nICJmb3IgZWFjaCIgdG8gZGVzY3JpYmUgc29tZXRoaWcgeW91IHdhbnQgdG8gZG8sIHlvdSB3aWxsIHByb2JhYmx5IHdhbnQgdG8gdXNlIGBncm91cF9ieSgpYCBmaXJzdC5dKGRwbHlyX2FydC9ncm91cGJ5X3BldC5wbmcpe3dpZHRoPTUwJX0KCiFbV2Ugb2Z0ZW4gdXNlIGBzdW1tYXJpemUoKWAgYWZ0ZXIgYSBgZ3JvdXBfYnkoKWAuIFRoaXMgd2lsbCBhZGQgYSBuZXcgdmFyaWFibGUgYW5kIGRlY3JlYXNlIHRoZSBudW1iZXIgb2Ygcm93cyBiYXNlZCBvbiBob3cgd2Ugc3VtbWFyaXplIHRoZSBkYXRhLiBMaWtlIGBtdXRhdGUoKWAsIHdlIGFsc28gbmVlZCB0byBnaXZlIHRoZSBkZXRhaWxzIG9mIGhvdyB0byBjb21wdXRlIHRoZSBuZXcgdmFyaWFibGUgaW5zaWRlIHRoZSBmdW5jdGlvbi5dKGRwbHlyX2FydC9zdW1tYXJpemVfcGV0LnBuZyl7d2lkdGg9NTAlfQoKIVtXZSBjYW4gYWxzbyBgZ3JvdXBfYnkoKWAgbW9yZSB0aGFuIG9uZSB2YXJpYWJsZSB3aGljaCB3aWxsIGdyb3VwIG9ic2VydmF0aW9ucyBieSBlYWNoIGNvbWJpbmF0aW9uIG9mIHZhbHVlcyBvZiB0aG9zZSB2YXJpYWJsZXMuXShkcGx5cl9hcnQvZ3JvdXBieV9jb21wbGV4LnBuZyl7d2lkdGg9NTAlfQoKIVtIZXJlIGlzIGFuIGV4YW1wbGUgb2YgdXNpbmcgYSBgc3VtbWFyaXplKClgIGFmdGVyIGEgYGdyb3VwX2J5KClgIHRoYXQgaW5jbHVkZXMgdHdvIHZhcmlhYmxlcy4gU2VlIHRoZSBleHRyYSBzZWN0aW9uIG9uIGBncm91cF9ieSgpYCBhbmQgYHN1bW1hcml6ZSgpYCBmb3IgbW9yZSBpbmZvcm1hdGlvbi5dKGRwbHlyX2FydC9zdW1tYXJpemVfY29tcGxleC5wbmcpe3dpZHRoPTUwJX0KCjwvY2VudGVyPgoKIyMjIFVzZWZ1bCBmdW5jdGlvbnMgYW5kIG9wZXJhdG9ycwoKVGhpcyB0YWJsZSBzaG93cyB0aGUgbG9naWNhbCBvcGVyYXRvcnMgb2Z0ZW4gdXNlZCB3aXRoIHRoZSBgZmlsdGVyKClgIHZlcmIuCgpPcGVyYXRvciAgfCBNZWFuaW5nCi0tLS0tLS0tLS18LS0tLS0tLS0KYD09YCAgICAgIHwgRXF1YWwgdG8KYD5gICAgICAgIHwgR3JlYXRlciB0aGFuCmA8YCAgICAgICB8IExlc3MgdGhhbgpgPj1gICAgICAgfCBHcmVhdGVyIHRoYW4gb3IgZXF1YWwgdG8KYDw9YCAgICAgIHwgTGVzcyB0aGFuIG9yIGVxdWFsIHRvCmAhPWAgICAgICB8IE5vdCBlcXVhbCB0bwpgJWluJWAgICAgfCBpbiAKYGlzLm5hYCAgIHwgaXMgYSBtaXNzaW5nIHZhbHVlIChOQSkKYCFpcy5uYWAgIHwgaXMgbm90IGEgbWlzc2luZyB2YWx1ZQpgJmAgICAgICAgfCBhbmQKYHxgICAgICAgIHwgb3IKCgpUaGUgdGFibGUgYmVsb3cgc2hvd3MgY29tbW9uIGZ1bmN0aW9ucyB5b3Ugd291bGQgdXNlIHdoZW4geW91IGFkZCBhIHZhcmlhYmxlIHVzaW5nIGBtdXRhdGUoKWAuIEZpbmQgbW9yZSBpbiB0aGUgYGRwbHlyYCBjaGVhdHNoZWV0IGxpbmtlZCBpbiBSZXNvdXJjZXMuCgpGdW5jdGlvbiAgfCBNZWFuaW5nCi0tLS0tLS0tLS18LS0tLS0tLS0KYCtgLCBgLWAsIGAqYCwgYC9gLCBgXmAgfCBBcml0aG1ldGljIG9wZXJhdGlvbnMKYD5gLCBgPGAsIGV0Yy4gIHwgTG9naWNhbCBvcGVyYXRvcnMgKHNlZSBhYm92ZSkKYGlmZWxzZSgpYCAgfCBVc2VkIHRvIGNyZWF0ZSBiaW5hcnkgdmFyaWFibGUgZnJvbSBub24tYmluYXJ5CmBsYWcoKWAgICAgfCBPZmZzZXQgZWxlbWVudHMgYnkgMSAgCmBjdW1zdW0oKWAgfCBjdW11bGF0aXZlIHN1bSgpCgpUaGUgdGFibGUgYmVsb3cgc2hvd3MgY29tbW9uIGZ1bmN0aW9ucyB5b3Ugd291bGQgdXNlIHdoZW4geW91IGFkZCBhIHN1bW1hcnkgdmFyaWFibGUgdXNpbmcgYHN1bW1hcml6ZSgpYC4gRmluZCBtb3JlIGluIHRoZSBgZHBseXJgIGNoZWF0c2hlZXQgbGlua2VkIGluIFJlc291cmNlcy4gV2l0aCBtYW55IG9mIHRoZXNlIGZ1bmN0aW9ucywgeW91IGNhbiBhZGQgYG5hLnJtID0gVFJVRWAgdG8gcmVtb3ZlIG1pc3NpbmcgdmFsdWVzLgoKRnVuY3Rpb24gIHwgTWVhbmluZwotLS0tLS0tLS0tfC0tLS0tLS0tCmBuKClgIHwgbnVtYmVyIG9mIHJvd3MKYG1lYW4oKWAgIHwgbWVhbiAKYG1lZGlhbigpYCB8IG1lZGlhbgpgc3VtKClgICAgfCBzdW0KYHNkKClgICAgfCBzdGFuZGFyZCBkZXZpYXRpb24KYElRUigpYCAgfCBpbnRlcnF1YXJ0aWxlIHJhbmdlCgojIyMgRGVtbyB2aWRlbwoKTmV4dCwgd2F0Y2ggdGhlIHZpZGVvIGJlbG93IHRoYXQgd2Fsa3MgdGhyb3VnaCBzb21lIGV4YW1wbGVzIGluIFIgU3R1ZGlvLiBKdXN0IGxpa2Ugd2l0aCB0aGUgYGdncGxvdCgpYCBtYXRlcmlhbCwgeW91IGNhbiBwcmFjdGljZSB0aGUgYGRwbHlyYCBwcm9ibGVtcyBhbG9uZyB3aXRoIG1lIGJ5IGRvd25sb2FkaW5nIHRoZSBSIE1hcmtkb3duIGZpbGUgYW5kIHdvcmtpbmcgdGhyb3VnaCB0aGVtLiBJZiB5b3UgZG8gdGhhdCwgeW91IHdpbGwgbGlrZWx5IGdldCBzb21ld2hhdCBkaWZmZXJlbnQgcmVzdWx0cyB0aGFuIHlvdSBzZWUgaW4gdGhlIHZpZGVvIHdoZW4gdXNpbmcgdGhlIGBnYXJkZW5faGFydmVzdGAgZGF0YSBiZWNhdXNlIHRoZSBkYXRhIGhhcyBjaGFuZ2VkIHNpbmNlIEkgbWFkZSB0aGUgdmlkZW8uIEF0IHRoZSB0aW1lIG9mIHRoZSB2aWRlbywgSSB3YXMgc3RpbGwgY29sbGVjdGluZyBkYXRhIGZyb20gdGhlIGdhcmRlbiA6KQoKPGlmcmFtZSB3aWR0aD0iNTYwIiBoZWlnaHQ9IjMxNSIgc3JjPSJodHRwczovL3d3dy55b3V0dWJlLmNvbS9lbWJlZC9QZ1p2cTdaUHZFRSIgZnJhbWVib3JkZXI9IjAiIGFsbG93PSJhY2NlbGVyb21ldGVyOyBhdXRvcGxheTsgZW5jcnlwdGVkLW1lZGlhOyBneXJvc2NvcGU7IHBpY3R1cmUtaW4tcGljdHVyZSIgYWxsb3dmdWxsc2NyZWVuPjwvaWZyYW1lPgoKW1ZvaWNldGhyZWFkOiBgZHBseXJgIGRlbW9dKGh0dHBzOi8vdm9pY2V0aHJlYWQuY29tL3NoYXJlLzE1MDc4OTczLykKCmBgYHtyLCBlY2hvPUZBTFNFfQpkb3dubG9hZF9maWxlKAogIHBhdGggPSAiLi4vMDJfZHBseXIvMDJfZHBseXJfZGVtb19ub19jb2RlLlJtZCIsCiAgYnV0dG9uX2xhYmVsID0gIkRvd25sb2FkIGRwbHlyIGRlbW8gZmlsZSAod2l0aG91dCBjb2RlKSIsCiAgYnV0dG9uX3R5cGUgPSAid2FybmluZyIsCiAgaGFzX2ljb24gPSBUUlVFLAogIGljb24gPSAiZmEgZmEtc2F2ZSIsCiAgc2VsZl9jb250YWluZWQgPSBGQUxTRQopCmBgYAoKYGBge3IsIGVjaG89RkFMU0V9CmRvd25sb2FkX2ZpbGUoCiAgcGF0aCA9ICIuLi8wMl9kcGx5ci8wMl9kcGx5cl9kZW1vLlJtZCIsCiAgYnV0dG9uX2xhYmVsID0gIkRvd25sb2FkIGRwbHlyIGRlbW8gZmlsZSAod2l0aCBjb2RlKSIsCiAgYnV0dG9uX3R5cGUgPSAiaW5mbyIsCiAgaGFzX2ljb24gPSBUUlVFLAogIGljb24gPSAiZmEgZmEtc2F2ZSIsCiAgc2VsZl9jb250YWluZWQgPSBGQUxTRQopCmBgYAoKIyMjIEEgc3BlY2lhbCBub3RlIGFib3V0IHRoZSBgZ3JvdXBfYnkoKWAgZnVuY3Rpb24KCkluIHRoZSBkZW1vIHZpZGVvLCBJIGRvbid0IHRoaW5rIEkgZGlkIGVub3VnaCB0byBlbXBoYXNpemUgdGhlIGdyb3VwaW5nIGJlaGF2aW9yIGFuZCBob3cgaXQgZGlmZmVycyBkZXBlbmRpbmcgb24gd2hpY2ggZnVuY3Rpb24gY29tZXMgYWZ0ZXIgaXQuIEkgaGlnaGxpZ2h0IHNvbWUga2V5IGJlaGF2aW9ycyBhbmQgZ2l2ZSBzb21lIHJlY29tbWVuZGF0aW9ucyBiZWxvdzoKCiogV2hlbiBgZ3JvdXBfYnkoKWAgaXMgZm9sbG93ZWQgYnkgYSBgc3VtbWFyaXplKClgLCB0aGUgZGF0YSB3aWxsIGJlIGdyb3VwZWQgb25lIGxldmVsIGhpZ2hlciB0aGFuIGluIHRoZSBgZ3JvdXBfYnkoKWAuIEZvciBleGFtcGxlLCBpbiB0aGUgY29kZSBiZWxvdyB0aGUgYGdyb3VwX3ZhcnMoKWAgZnVuY3Rpb24gdGVsbHMgdXMgYnkgd2hpY2ggdmFyaWFibGVzIHRoZSBkYXRhIGFyZSBncm91cGVkLiBJbiB0aGUgZmlyc3Qgc2V0IG9mIGNvZGUsIHRoZXkgYXJlIGdyb3VwZWQgYnkgYHZlZ2V0YWJsZWAgaW4gdGhlIGVuZDsgYnV0IGluIHRoZSBzZWNvbmQsIHRoZXkgYXJlIGdyb3VwZWQgYnkgYGRhdGVgIGluIHRoZSBlbmQuIFRoaXMgYWxzbyBtZWFucyB0aGF0IGlmIHRoZXJlIGlzIG9ubHkgb25lIGdyb3VwaW5nIHZhcmlhYmxlLCBhZnRlciBhIGBzdW1tYXJpemUoKWAsIHRoZSBkYXRhIGFyZSBubyBsb25nZXIgZ3JvdXBlZC4gVGhpcyBpcyBpbGx1c3RyYXRlZCBpbiB0aGUgdGhpcmQgcGllY2Ugb2YgY29kZSAobm90ZSB0aGF0IGl0IG91dHB1dHMgYGNoYXJhY3RlcigwKWAgd2hpY2ggbWVhbnMgdGhlcmUgaXNuJ3Qgb25lKS4gIAoKYGBge3J9CmdhcmRlbl9oYXJ2ZXN0ICU+JSAKICBncm91cF9ieSh2ZWdldGFibGUsIGRhdGUpICU+JSAKICBzdW1tYXJpemUodG90X2hhcnZlc3QgPSBzdW0od2VpZ2h0KSkgJT4lIAogIGdyb3VwX3ZhcnMoKQoKZ2FyZGVuX2hhcnZlc3QgJT4lIAogIGdyb3VwX2J5KGRhdGUsIHZlZ2V0YWJsZSkgJT4lIAogIHN1bW1hcml6ZSh0b3RfaGFydmVzdCA9IHN1bSh3ZWlnaHQpKSAlPiUgCiAgZ3JvdXBfdmFycygpCgpnYXJkZW5faGFydmVzdCAlPiUgCiAgZ3JvdXBfYnkoZGF0ZSkgJT4lIAogIHN1bW1hcml6ZSh0b3RfaGFydmVzdCA9IHN1bSh3ZWlnaHQpKSAlPiUgCiAgZ3JvdXBfdmFycygpCmBgYAoKKiBVc2luZyBgbXV0YXRlKClgIGFmdGVyIGEgYGdyb3VwX2J5KClgIGRvZXMgbm90IGNoYW5nZSB0aGUgZ3JvdXBpbmcuIEluIHRoZSBjb2RlIGJlbG93LCB3ZSBzdGlsbCBzZWUgdGhhdCB0aGUgZGF0YSBhcmUgZ3JvdXBlZCBieSBib3RoIGB2ZWdldGFibGVgIGFuZCBgZGF0ZWAuCgpgYGB7cn0KZ2FyZGVuX2hhcnZlc3QgJT4lIAogIGdyb3VwX2J5KHZlZ2V0YWJsZSwgZGF0ZSkgJT4lIAogIG11dGF0ZSh0b3RfaGFydmVzdCA9IHN1bSh3ZWlnaHQpKSAlPiUgCiAgZ3JvdXBfdmFycygpCmBgYAoKKiBUaGUgYHVuZ3JvdXAoKWAgZnVuY3Rpb24gcmVtb3ZlcyBhbGwgZ3JvdXBpbmcgYXMgc2VlbiBpbiB0aGUgdHdvIHBpZWNlcyBvZiBjb2RlIGJlbG93LgoKYGBge3J9CmdhcmRlbl9oYXJ2ZXN0ICU+JSAKICBncm91cF9ieSh2ZWdldGFibGUsIGRhdGUpICU+JSAKICBzdW1tYXJpemUodG90X2hhcnZlc3QgPSBzdW0od2VpZ2h0KSkgJT4lIAogIHVuZ3JvdXAoKSAlPiUgCiAgZ3JvdXBfdmFycygpCgpnYXJkZW5faGFydmVzdCAlPiUgCiAgZ3JvdXBfYnkodmVnZXRhYmxlLCBkYXRlKSAlPiUgCiAgbXV0YXRlKHRvdF9oYXJ2ZXN0ID0gc3VtKHdlaWdodCkpICU+JSAKICB1bmdyb3VwKCkgJT4lIAogIGdyb3VwX3ZhcnMoKQpgYGAKCiogSXQgaXMgaW1wb3J0YW50IHRvIHBheSBjbG9zZSBhdHRlbnRpb24gdG8gaG93IGRhdGEgYXJlIGdyb3VwZWQgYWZ0ZXIgdXNpbmcgYGdyb3VwX2J5KClgIGZvbGxvd2VkIGJ5IGFub3RoZXIgZnVuY3Rpb24uIFRoZSBiZWhhdmlvciBvZiB0aGUgZ3JvdXBpbmcgZGVwZW5kcyBvbiB3aGljaCBmdW5jdGlvbiBmb2xsb3dzIHRoZSBgZ3JvdXBfYnkoKWAuIFVzZSB0aGUgYGdyb3VwX3ZhcnMoKWAgZnVuY3Rpb24gdG8gY2hlY2suIFlvdSBjYW4gYWx3YXlzIGB1bmdyb3VwKClgIGFuZCB0aGVuIHVzZSBgZ3JvdXBfYnkoKWAgYWdhaW4gb3IgYWRkIGEgYGdyb3VwX2J5KClgIHRvIGV4cGxpY2l0bHkgYXNzdXJlIHRoZSBkYXRhIGFyZSBncm91cGVkIGluIHRoZSB3YXkgeW91IGludGVuZCB0aGVtIHRvIGJlLiAgCgoKIyMjIFJlc291cmNlcwoKKiBbUjREU10oaHR0cHM6Ly9yNGRzLmhhZC5jby5uei90cmFuc2Zvcm0uaHRtbCkgY2hhcHRlcnMgNSBhbmQgMTggKGVzcGVjaWFsbHkgWzE4LjIuNF0oaHR0cHM6Ly9yNGRzLmhhZC5jby5uei9waXBlcy5odG1sI3VzZS10aGUtcGlwZSkpCiogW2RwbHlyIGNoZWF0c2hlZXRdKGh0dHBzOi8vcnN0dWRpby5jb20vcmVzb3VyY2VzL2NoZWF0c2hlZXRzLykgKHNlYXJjaCBmb3IgVHJhbnNmb3JtYXRpb24pICAKKiBbZHBseXIgZG9jdW1lbnRhdGlvbl0oaHR0cHM6Ly9kcGx5ci50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS9pbmRleC5odG1sKSAgCiogW2dyb3VwX2J5IGRvY3VtZW50YXRpb25dKGh0dHBzOi8vZHBseXIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2UvZ3JvdXBfYnkuaHRtbCkocGFydCBvZiBgZHBseXJgIGRvY3VtZW50YXRpb24pCgoKIyMjIFlvdXIgdHVybiEKCiMjIyMgRXhlcmNpc2UgMTogYHNlbGVjdCgpYAoKU2VsZWN0IGB2ZWdldGFibGVgLCBgZGF0ZWAsIGFuZCBgd2VpZ2h0YCBmcm9tIHRoZSBgZ2FyZGVuX2hhcnZlc3RgIGRhdGEuIEkgaGF2ZSBzdGFydGVkIHRoZSBjb2RlIGZvciB5b3UgYmVsb3cuCgpgYGB7ciBnYXJkZW4tc2VsZWN0LCBldmFsPUZBTFNFfQpnYXJkZW5faGFydmVzdCAjV2hhdCBkbyBJIG5lZWQgdG8gcHV0IGhlcmU/CiAgc2VsZWN0KCkKYGBgCgoKIyMjIyBFeGVyY2lzZSAyYTogYG11dGF0ZSgpYAoKQWRkIGEgdmFyaWFibGUgZm9yIHdlaWdodCBpbiBraWxvZ3JhbXMsIGB3ZWlnaHRfa2dgLiBPbmUga2lsb2dyYW0gaXMgMTAwMCBncmFtcy4gSSBzdGFydGVkIHRoZSBjb2RlIGJlbG93LgoKYGBge3IgZ2FyZGVuLWtnLCBldmFsPUZBTFNFfQpnYXJkZW5faGFydmVzdCAjV2hhdCBkbyBJIG5lZWQgdG8gcHV0IGhlcmU/CiAgbXV0YXRlKCkKYGBgCgojIyMjIEV4ZXJjaXNlIDJiOiBgbXV0YXRlKClgCgpLZWVwIHRoZSBgd2VpZ2h0X2tnYCB2YXJpYWJsZSBmcm9tIHRoZSBwcmV2aW91cyBwcm9ibGVtIGFuZCBhbHNvIGFkZCBhIHZhcmlhYmxlIHRvIHRoZSBgZ2FyZGVuX2hhcnZlc3RgIGRhdGEgY2FsbGVkIGBkYXlfb2Zfd2Vla2AgdGhhdCByZXR1cm5zIHRoZSBkYXkgb2YgdGhlIHdlZWsuIEhJTlQ6IFVzZSB0aGUgZnVuY3Rpb24gYHdkYXkoKWAgYW5kIGFkZCBhbiBhcmd1bWVudCB0byB0aGF0IGZ1bmN0aW9uIHRoYXQgaXMgYGxhYmVsPVRSVUVgLiAKCmBgYHtyIGdhcmRlbi1kYXl9CgpgYGAKCiMjIyMgRXhlcmNpc2UgM2E6IGBmaWx0ZXIoKWAKCkZpbHRlciB0aGUgYGdhcmRlbl9oYXJ2ZXN0YCBkYXRhIHRvIG9ic2VydmF0aW9ucyB0aGF0IGhhdmUgd2VpZ2h0cyBsZXNzIHRoYW4gNTAgZ3JhbXMuIAoKYGBge3IgZ2FyZGVuLWZpbHRlcn0KCmBgYAoKIyMjIyBFeGVyY2lzZSAzYjogYGZpbHRlcigpYAoKRmlsdGVyIHRoZSBgZ2FyZGVuX2hhcnZlc3RgIGRhdGEgdG8gcGVhcyBhbmQgYmVhbnMgd2l0aCB3ZWlnaHRzIGxhcmdlciB0aGFuIDQwIGdyYW1zLgoKYGBge3IgZmlsdGVyLXBlYXMtYmVhbnN9CgpgYGAKCiMjIyMgRXhlcmNpc2UgNGE6IGBhcnJhbmdlKClgCgpPcmRlciB0aGUgb2JzZXJ2YXRpb25zIGluIHRoZSBgZ2FyZGVuX2hhcnZlc3RgIGRhdGEgZnJvbSBsYXJnZXN0IHRvIHNtYWxsZXN0IHdlaWdodC4KCmBgYHtyIGFycmFuZ2UtZ2FyZGVufQoKYGBgCgojIyMjIEV4ZXJjaXNlIDRiOiBgYXJyYW5nZSgpYAoKT3JkZXIgdGhlIG9ic2VydmF0aW9ucyBpbiB0aGUgYGdhcmRlbl9oYXJ2ZXN0YCBkYXRhIGZyb20gbGFyZ2VzdCB0byBzbWFsbGVzdCB3ZWlnaHQgb24gZWFjaCBkYXRlLgoKYGBge3IgYXJyYW5nZS1nYXJkZW4yfQoKYGBgCgojIyMjIEV4ZXJjaXNlIDVhOiBgc3VtbWFyaXplKClgCgpGaW5kIHRoZSB0b3RhbCB3ZWlnaHQgaW4gZ3JhbXMgYW5kIGhvdyBtYW55IHJvd3Mgb2YgZGF0YSBhcmUgaW4gdGhlIGBnYXJkZW5faGFydmVzdGAgZGF0YS4KCmBgYHtyIHN1bW1hcml6ZTF9CgpgYGAKCiMjIyMgRXhlcmNpc2UgNWI6IGBzdW1tYXJpemUoKWAgd2l0aCBgZ3JvdXBfYnkoKWAKCkZpbmQgdGhlIHRvdGFsIHdlaWdodCBpbiBncmFtcyBoYXJ2ZXN0ZWQgZm9yIGVhY2ggZGF0ZS4gQWZ0ZXIgZG9pbmcgdG8gYHN1bW1hcml6ZSgpYCwgaG93IGFyZSB0aGUgZGF0YSBncm91cGVkPwoKYGBge3Igc3VtLWdyb3VwfQoKYGBgCgojIyMjIEV4ZXJjaXNlIDY6IGNvbWJpbmluZyBgZHBseXJgIHZlcmJzCgpJIGxvdmUgdG9tYXRvZXMuIFdlbGwsIHRydXRoZnVsbHksIEkgbG92ZSB0aGluZ3MgbWFkZSBvdXQgb2YgdG9tYXRvZXMgLSBzcGFnaGV0dGkgc2F1Y2UsIHNhbHNhLCBzb3VwcywgYW5kIGV2ZW4ga2V0Y2h1cC4gSSBhbHdheXMgd2FudCB0byBrbm93IHdoaWNoIHZhcmlldHkgb2YgdG9tYXRvIGlzIG1vc3QgcHJvZHVjdGl2ZS4gIEluIHRoaXMgZXhlcmNpc2UsIHN0YXJ0IHdpdGggdGhlIGBnYXJkZW5faGFydmVzdGAgZGF0YSwgZmlsdGVyIHRvIHRvbWF0b2VzLCBmaW5kIHRoZSB0b3RhbCB3ZWlnaHQgZm9yIGVhY2ggdmFyaWV0eSwgY29tcHV0ZSBhIG5ldyB2YXJpYWJsZSB0byBjb252ZXJ0IHRoZSB3ZWlnaHRzIGZyb20gZ3JhbXMgdG8gcG91bmRzLCBhbmQgbGFzdGx5IHNvcnQgdGhlIGRhdGEgZnJvbSBsYXJnZXN0IHRvIHNtYWxsZXN0IHRvdGFsIHdlaWdodCBpbiBwb3VuZHMuIFdoaWNoIHZhcmlldHkgaXMgYmVzdD8gSXMgdGhlcmUgYW55IGluZm9ybWF0aW9uIG1pc3Npbmc/IFRoaW5rIGFib3V0IChhbmQgY2hlY2shKSBob3cgeW91ciBkYXRhIGFyZSBncm91cGVkIGVhY2ggc3RlcCBvZiB0aGUgd2F5LgoKYGBge3IgY29tYm99CgpgYGAKCiMjIyMgRXhlcmNpc2UgNzogY29tYmluaW5nIGBkcGx5cmAgdmVyYnMgYW5kIGBnZ3Bsb3QoKWAKCkknbSBjdXJpb3VzIGlmIHRoZXJlIGFyZSBjZXJ0YWluIGRheXMgZHVyaW5nIHRoZSB3ZWVrIHdoZXJlIEkgaGFydmVzdCBtb3JlIG9yIGxlc3MuIEluIHRoaXMgZXhlcmNpc2UsIHN0YXJ0IHdpdGggdGhlIGBnYXJkZW5faGFydmVzdGAgZGF0YSwgZmluZCB0aGUgZGFpbHkgaGFydmVzdCBpbiBncmFtcyBmb3IgZWFjaCBkYXRlLCBjcmVhdGUgdHdvIG5ldyB2YXJpYWJsZXM6IDEuIHRoZSBkYWlseSBoYXJ2ZXN0IGluIHBvdW5kcyBhbmQgMi4gZGF5IG9mIHRoZSB3ZWVrLCBwbG90IHRoZSBkYXRhIHNvIGZvciBlYWNoIGRheSBvZiB0aGUgd2VlayAob24gdGhlIHktYXhpcykgYSBib3hwbG90IG9mIHRoZSBkYWlseSBoYXJ2ZXN0IGluIHBvdW5kcyBpcyBjcmVhdGVkLgoKYGBge3IgYWxsLXRvZ2V0aGVyfQoKYGBgCgoKIyMgRXhlcmNpc2Ugc29sdXRpb25zCgojIyMjIEV4ZXJjaXNlIDFhOiBTY2F0dGVycGxvdHMKCmBgYHtyIHBlbmd1aW4tc2NhdHRlci1zb2x1dGlvbn0KcGVuZ3VpbnMgJT4lIAogIGdncGxvdChhZXMoeCA9IGJpbGxfbGVuZ3RoX21tLCAKICAgICAgICAgICAgIHkgPSBiaWxsX2RlcHRoX21tKSkgKwogIGdlb21fcG9pbnQoKQpgYGAKCiMjIyMgRXhlcmNpc2UgMWI6IFNjYXR0ZXJwbG90cwoKYGBge3IgcGVuZ3Vpbi1zY2F0dGVyMi1zb2x1dGlvbn0KcGVuZ3VpbnMgJT4lIAogIGdncGxvdChhZXMoeCA9IGJpbGxfbGVuZ3RoX21tLCAKICAgICAgICAgICAgIHkgPSBiaWxsX2RlcHRoX21tLAogICAgICAgICAgICAgY29sb3IgPSBzcGVjaWVzKSkgKwogIGdlb21fcG9pbnQoKQpgYGAKCiMjIyMgQ0hBTExFTkdFOiBTY2F0dGVycGxvdHMgCgpgYGB7ciBwZW5ndWluLXNjYXR0ZXIzLXNvbHV0aW9ufQpwZW5ndWlucyAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gYmlsbF9sZW5ndGhfbW0sIAogICAgICAgICAgICAgeSA9IGJpbGxfZGVwdGhfbW0sCiAgICAgICAgICAgICBjb2xvciA9IHNwZWNpZXMpKSArCiAgZ2VvbV9wb2ludChhbHBoYSA9IC41LCBzaXplID0gLjUpCmBgYAoKIyMjIyBFeGVyY2lzZSAyYTogSGlzdG9ncmFtcwoKYGBge3IgcGVuZ3Vpbi1oaXN0LXNvbHV0aW9ufQpwZW5ndWlucyAlPiUgCiAgZ2dwbG90KGFlcyhmbGlwcGVyX2xlbmd0aF9tbSkpICsKICBnZW9tX2hpc3RvZ3JhbSgpCmBgYAoKIyMjIyBFeGVyY2lzZSAyYjogSGlzdG9ncmFtcwoKYGBge3IgcGVuZ3Vpbi1oaXN0LWZhY2V0LXNvbHV0aW9ufQpwZW5ndWlucyAlPiUgCiAgZ2dwbG90KGFlcyhmbGlwcGVyX2xlbmd0aF9tbSkpICsKICBnZW9tX2hpc3RvZ3JhbSgpICsKICBmYWNldF93cmFwKHZhcnMoc3BlY2llcyksIG5jb2wgPSAxKQpgYGAKCiMjIyMgRXhlcmNpc2UgM2E6IEJhcnBsb3RzIAoKYGBge3IgcGVuZ3Vpbi1iYXItc29sdXRpb259CnBlbmd1aW5zICU+JSAKICBnZ3Bsb3QoYWVzKHg9eWVhcikpICsKICBnZW9tX2JhcihmaWxsID0gImxpZ2h0Ymx1ZSIpCmBgYAoKIyMjIyBDSEFMTEVOR0U6IEJhcnBsb3RzIAoKYGBge3IgdG9tYXRvZXMtYmFyLXNvbHV0aW9ufQp0b21hdG9lcyAlPiUgCiAgZ2dwbG90KGFlcyh5PWZjdF9yZXYoZmN0X2luZnJlcSh2YXJpZXR5KSkpKSArCiAgZ2VvbV9iYXIoZmlsbCA9ICJ0b21hdG80IikgKwogIGxhYnModGl0bGUgPSAiVG9tYXRvZXMiLCAKICAgICAgIHN1YnRpdGxlID0gIiMgb2YgZGF5cyBlYWNoIHZhcmlldHkgaGFzIGJlZW4gaGFydmVzdGVkIiwKICAgICAgIHggPSAiIiwKICAgICAgIHkgPSAiIikKYGBgCgojIyMjIEV4ZXJjaXNlIDQ6IEJveHBsb3RzCgpgYGB7ciBwZW5ndWluLWJveC1zb2x1dGlvbn0KcGVuZ3VpbnMgJT4lIAogIGdncGxvdChhZXMoeCA9IGZsaXBwZXJfbGVuZ3RoX21tLCB5ID0gc3BlY2llcykpICsKICBnZW9tX2JveHBsb3QoKQpgYGAKCiMjIyMgRXhlcmNpc2UgNWE6IExpbmUgZ3JhcGhzCgpgYGB7ciB0b21hdG9lcy1saW5lLXNvbHV0aW9ufQp0b21hdG9lc193dF9kYXRlICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBkYXRlLCB5ID0gZGFpbHlfd3RfZykpICsKICBnZW9tX2xpbmUoKQpgYGAKCiMjIyMgRXhlcmNpc2UgNWI6IExpbmUgZ3JhcGhzCgpgYGB7ciB0b21hdG8tdmFyaWV0eS1saW5lLXNvbHV0aW9ufQp0b21hdG9fdmFyaWV0eV9kYWlseSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gZGF0ZSwgeSA9IGRhaWx5X3d0X2csIGNvbG9yID0gdmFyaWV0eSkpICsKICBnZW9tX2xpbmUoKQpgYGAKCiMjIyMgRXhlcmNpc2UgMTogYHNlbGVjdCgpCgpgYGB7ciBnYXJkZW4tc2VsZWN0LXNvbHV0aW9ufQpnYXJkZW5faGFydmVzdCAlPiUgCiAgc2VsZWN0KHZlZ2V0YWJsZSwgZGF0ZSwgd2VpZ2h0KQpgYGAKCiMjIyMgRXhlcmNpc2UgMmE6IGBtdXRhdGUoKWAKCmBgYHtyIGdhcmRlbi1rZy1zb2x1dGlvbn0KZ2FyZGVuX2hhcnZlc3QgJT4lIAogIG11dGF0ZSh3ZWlnaHRfa2cgPSB3ZWlnaHQvMTAwMCkKYGBgCgojIyMjIEV4ZXJjaXNlIDJiOiBgbXV0YXRlKClgCgpgYGB7ciBnYXJkZW4tZGF5LXNvbHV0aW9ufQpnYXJkZW5faGFydmVzdCAlPiUgCiAgbXV0YXRlKHdlaWdodF9rZyA9IHdlaWdodC8xMDAwLAogICAgICAgICBkYXlfb2Zfd2VlayA9IHdkYXkoZGF0ZSwgbGFiZWwgPSBUUlVFKSkKYGBgCgojIyMjIEV4ZXJjaXNlIDNhOiBgZmlsdGVyKClgCgpgYGB7ciBnYXJkZW4tZmlsdGVyLXNvbHV0aW9ufQpnYXJkZW5faGFydmVzdCAlPiUKICBmaWx0ZXIod2VpZ2h0IDwgNTApCmBgYAoKIyMjIyBFeGVyY2lzZSAzYjogYGZpbHRlcigpYAoKYGBge3IgZmlsdGVyLXBlYXMtYmVhbnMtc29sdXRpb259CmdhcmRlbl9oYXJ2ZXN0ICU+JSAKICBmaWx0ZXIodmVnZXRhYmxlICVpbiUgYygicGVhcyIsICJiZWFucyIpLAogICAgICAgICB3ZWlnaHQgPiA0MCkKYGBgCgojIyMjIEV4ZXJjaXNlIDRhOiBgYXJyYW5nZSgpYAoKYGBge3IgYXJyYW5nZS1nYXJkZW4tc29sdXRpb259CmdhcmRlbl9oYXJ2ZXN0ICU+JSAKICBhcnJhbmdlKGRlc2Mod2VpZ2h0KSkKYGBgCgojIyMjIEV4ZXJjaXNlIDRiOiBgYXJyYW5nZSgpYAoKYGBge3IgYXJyYW5nZS1nYXJkZW4yLXNvbHV0aW9ufQpnYXJkZW5faGFydmVzdCAlPiUgCiAgYXJyYW5nZShkYXRlLCBkZXNjKHdlaWdodCkpCmBgYAoKIyMjIyBFeGVyY2lzZSA1YTogYHN1bW1hcml6ZSgpYAoKYGBge3Igc3VtbWFyaXplMS1zb2x1dGlvbn0KZ2FyZGVuX2hhcnZlc3QgJT4lIAogIHN1bW1hcml6ZShucm93cyA9IG4oKSwKICAgICAgICAgICAgdG90YWxfd2VpZ2h0ID0gc3VtKHdlaWdodCkpCmBgYAoKIyMjIyBFeGVyY2lzZSA1YjogYHN1bW1hcml6ZSgpYCB3aXRoIGBncm91cF9ieSgpYAoKYGBge3Igc3VtLWdyb3VwLXNvbHV0aW9ufQpnYXJkZW5faGFydmVzdCAlPiUgCiAgZ3JvdXBfYnkoZGF0ZSkgJT4lIAogIHN1bW1hcml6ZSh0b3RhbF93ZWlnaHQgPSBzdW0od2VpZ2h0KSkKYGBgCgpUbyBleGFtaW5lIGhvdyBkYXRhIGFyZSBncm91cGVkOgoKYGBge3J9CmdhcmRlbl9oYXJ2ZXN0ICU+JSAKICBncm91cF9ieShkYXRlKSAlPiUgCiAgc3VtbWFyaXplKHRvdGFsX3dlaWdodCA9IHN1bSh3ZWlnaHQpKSAlPiUgCiAgZ3JvdXBfdmFycygpCmBgYAoKIyMjIyBFeGVyY2lzZSA2OiBjb21iaW5pbmcgYGRwbHlyYCB2ZXJicwoKYGBge3IgY29tYm8tc29sdXRpb259CmdhcmRlbl9oYXJ2ZXN0ICU+JSAKICBmaWx0ZXIodmVnZXRhYmxlID09ICJ0b21hdG9lcyIpICU+JSAKICBncm91cF9ieSh2YXJpZXR5KSAlPiUgCiAgc3VtbWFyaXplKHRvdGFsX3dlaWdodF9nID0gc3VtKHdlaWdodCkpICU+JSAKICBtdXRhdGUodG90YWxfd2VpZ2h0X2xiID0gdG90YWxfd2VpZ2h0X2cqMC4wMDIyMDQ2MikgJT4lIAogIGFycmFuZ2UoZGVzYyh0b3RhbF93ZWlnaHRfbGIpKQpgYGAKCiMjIyMgRXhlcmNpc2UgNzogY29tYmluaW5nIGBkcGx5cmAgdmVyYnMgYW5kIGBnZ3Bsb3QoKWAKCmBgYHtyIGFsbC10b2dldGhlci1zb2x1dGlvbn0KZ2FyZGVuX2hhcnZlc3QgJT4lIAogIGdyb3VwX2J5KGRhdGUpICU+JSAKICBzdW1tYXJpemUoZGFpbHlfaGFydmVzdF9nID0gc3VtKHdlaWdodCkpICU+JSAKICBtdXRhdGUoZGFpbHlfaGFydmVzdF9sYiA9IGRhaWx5X2hhcnZlc3RfZyowLjAwMjIwNDYyLAogICAgICAgICBkYXlfb2Zfd2VlayA9IHdkYXkoZGF0ZSwgbGFiZWw9VFJVRSkpICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBkYWlseV9oYXJ2ZXN0X2xiLCB5ID0gZGF5X29mX3dlZWspKSArIAogIGdlb21fYm94cGxvdCgpCmBgYAoKCgoK